Java導(dǎo)出Execl疑難點(diǎn)處理的實(shí)現(xiàn)
一.背景
最近業(yè)務(wù)需求需要導(dǎo)出Execl,最終做出的效果如下,中間牽扯到大量的數(shù)據(jù)計(jì)算。
二.疑難問(wèn)題分析


問(wèn)題1:跨單元格處理及邊框設(shè)置
問(wèn)題2:自定義背景顏色添加
問(wèn)題3:單元格中部分文字設(shè)置顏色
問(wèn)題4:高度自適應(yīng)處理
三.問(wèn)題解決
在處理整個(gè)Excel導(dǎo)出中總結(jié)了很多。
整個(gè)開發(fā)過(guò)程使用的是Apache POI
pom.xml
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.8</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.8</version> </dependency>
3.1 HSSFworkbook,XSSFworkbook選哪個(gè)
最開始我沿用的是之前開發(fā)用的,HSSFworkbook最后發(fā)現(xiàn),HSSFworkbook在處理,自定義單元格背景顏色比較復(fù)雜,最后換成了XSSFworkbook。
HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,擴(kuò)展名是.xls;
XSSFWorkbook:是操作Excel2007后的版本,擴(kuò)展名是.xlsx;
所以在這里我推薦使用XSSFWorkbook
3.2跨單元格及邊框設(shè)置
//創(chuàng)建第一行,索引是從0開始的
row = sheet.createRow(0);
//創(chuàng)建第一個(gè)單元格
XSSFCell cell0 = row.createCell(0);
//設(shè)置單元格文字
cell0.setCellValue("姓名");
//設(shè)置單元格樣式
cell0.setCellStyle(cellStyleHead);
//跨單元格設(shè)置
//參數(shù)為 firstRow, lastRow, firstCol, lastCol
CellRangeAddress cellRange1 = new CellRangeAddress(0, 1, 0, 0);
sheet.addMergedRegion(cellRange1);
//注意如果直接在下面寫設(shè)置邊框的樣式,可能會(huì)出現(xiàn)邊框覆蓋不全的情況,可能是樣式覆蓋問(wèn)題
//所以應(yīng)該在數(shù)據(jù)渲染完成之后,在代碼的最后寫跨單元格邊框設(shè)置,這是非常重要的
調(diào)用設(shè)置邊框
//在數(shù)據(jù)渲染完成,調(diào)用封裝的邊框設(shè)置方法 setRegionStyle(wb, sheet, cellRange1);
設(shè)置邊框方法
/**
* 合并單元格之后設(shè)置邊框
*
* @param wb XSSFWorkbook對(duì)象
* @param sheet sheet
* @param region region
*/
static void setRegionStyle(XSSFWorkbook wb, XSSFSheet sheet, CellRangeAddress region) {
RegionUtil.setBorderTop(1, region, sheet, wb);
RegionUtil.setBorderBottom(1, region, sheet, wb);
RegionUtil.setBorderLeft(1, region, sheet, wb);
RegionUtil.setBorderRight(1, region, sheet, wb);
}
3.3自定義背景顏色設(shè)置
因?yàn)閜oi自帶的顏色索引可能不滿足我們開發(fā)的需求,需要自定義樣色
//創(chuàng)建單元格樣式 XSSFCellStyle cellStyleContent = wb.createCellStyle(); //創(chuàng)建背景顏色 226, 239, 218 對(duì)應(yīng)的就是RGB顏色 紅綠藍(lán) cellStyleContent.setFillForegroundColor(new XSSFColor(new java.awt.Color(226, 239, 218))); //填充m cellStyleContent.setFillPattern(CellStyle.SOLID_FOREGROUND);
3.4設(shè)置單元格中部分字體顏色
XSSFRichTextString ts = new XSSFRichTextString("123456\r\n789");
XSSFFont font2 = wb.createFont();
//字體高度
font2.setFontHeightInPoints((short) 10);
// 字體
font2.setFontName("宋體");
//字體顏色
font2.setColor(HSSFColor.GREEN.index);
//那些字體要設(shè)置顏色,
//int startIndex 開始索引
//int endIndex 結(jié)束索引
// Font font 字體
ts.applyFont(5, ts.length(), font2);
3.5高度自適應(yīng)
封裝的工具類如下,需要在數(shù)據(jù)渲染完的每行,調(diào)用如下工具類
//高度自適應(yīng) //XSSFRow row; ExcelUtil.calcAndSetRowHeigt(row);
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author Created by niugang on 2020/3/13/13:34
*/
public class ExcelUtil {
private ExcelUtil() {
throw new IllegalStateException("Utility class");
}
/**
* 根據(jù)行內(nèi)容重新計(jì)算行高
*
* @param sourceRow sourceRow
*/
public static void calcAndSetRowHeigt(XSSFRow sourceRow) {
for (int cellIndex = sourceRow.getFirstCellNum(); cellIndex <= sourceRow.getPhysicalNumberOfCells(); cellIndex++) {
//行高
double maxHeight = sourceRow.getHeight();
XSSFCell sourceCell = sourceRow.getCell(cellIndex);
//單元格的內(nèi)容
String cellContent = getCellContentAsString(sourceCell);
if (null == cellContent || "".equals(cellContent)) {
continue;
}
//單元格的寬高及單元格信息
Map<String, Object> cellInfoMap = getCellInfo(sourceCell);
Integer cellWidth = (Integer) cellInfoMap.get("width");
Integer cellHeight = (Integer) cellInfoMap.get("height");
if (cellHeight > maxHeight) {
maxHeight = cellHeight;
}
XSSFCellStyle cellStyle = sourceCell.getCellStyle();
//sourceRow.getSheet().getWorkbook()
XSSFFont font = cellStyle.getFont();
//字體的高度
short fontHeight = font.getFontHeight();
//cell內(nèi)容字符串總寬度
double cellContentWidth = cellContent.getBytes().length * 2 * 256;
//字符串需要的行數(shù) 不做四舍五入之類的操作
double stringNeedsRows = cellContentWidth / cellWidth;
//小于一行補(bǔ)足一行
if (stringNeedsRows < 1.0) {
stringNeedsRows = 1.0;
}
//需要的高度 (Math.floor(stringNeedsRows) - 1) * 40 為兩行之間空白高度
double stringNeedsHeight = (double) fontHeight * stringNeedsRows;
//需要重設(shè)行高
if (stringNeedsHeight > maxHeight) {
maxHeight = stringNeedsHeight;
//超過(guò)原行高三倍 則為5倍 實(shí)際應(yīng)用中可做參數(shù)配置
if (maxHeight / cellHeight > 5) {
maxHeight = 5 * cellHeight;
}
//最后取天花板防止高度不夠
maxHeight = Math.ceil(maxHeight);
//重新設(shè)置行高 同時(shí)處理多行合并單元格的情況
Boolean isPartOfRowsRegion = (Boolean) cellInfoMap.get("isPartOfRowsRegion");
if (isPartOfRowsRegion.equals(Boolean.TRUE)) {
Integer firstRow = (Integer) cellInfoMap.get("firstRow");
Integer lastRow = (Integer) cellInfoMap.get("lastRow");
//平均每行需要增加的行高
double addHeight = (maxHeight - cellHeight) / (lastRow - firstRow + 1);
for (int i = firstRow; i <= lastRow; i++) {
double rowsRegionHeight = sourceRow.getSheet().getRow(i).getHeight() + addHeight;
rowsRegionHeight=rowsRegionHeight+10;
sourceRow.getSheet().getRow(i).setHeight((short) rowsRegionHeight);
}
} else {
maxHeight=maxHeight+10;
sourceRow.setHeight((short) maxHeight);
}
}
}
}
/**
* 解析一個(gè)單元格得到數(shù)據(jù)
*
* @param cell cell
* @return String
*/
private static String getCellContentAsString(XSSFCell cell) {
final String strZero =".0";
if (null == cell) {
return "";
}
String result = "";
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC:
String s = String.valueOf(cell.getNumericCellValue());
if (s != null) {
if (s.endsWith(strZero)) {
s = s.substring(0, s.length() - 2);
}
}
result = s;
break;
case Cell.CELL_TYPE_STRING:
result = String.valueOf(cell.getStringCellValue()).trim();
break;
case Cell.CELL_TYPE_BLANK:
break;
case Cell.CELL_TYPE_BOOLEAN:
result = String.valueOf(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_ERROR:
break;
default:
break;
}
return result;
}
/**
* 獲取單元格及合并單元格的寬度
*
* @param cell cell
* @return Map<String , Object>
*/
private static Map<String, Object> getCellInfo(XSSFCell cell) {
XSSFSheet sheet = cell.getSheet();
int rowIndex = cell.getRowIndex();
int columnIndex = cell.getColumnIndex();
boolean isPartOfRegion = false;
int firstColumn = 0;
int lastColumn = 0;
int firstRow = 0;
int lastRow = 0;
int sheetMergeCount = sheet.getNumMergedRegions();
for (int i = 0; i < sheetMergeCount; i++) {
CellRangeAddress ca = sheet.getMergedRegion(i);
firstColumn = ca.getFirstColumn();
lastColumn = ca.getLastColumn();
firstRow = ca.getFirstRow();
lastRow = ca.getLastRow();
if (rowIndex >= firstRow && rowIndex <= lastRow) {
if (columnIndex >= firstColumn && columnIndex <= lastColumn) {
isPartOfRegion = true;
break;
}
}
}
Map<String, Object> map = new HashMap<>(16);
int width = 0;
int height = 0;
boolean isPartOfRowsRegion = false;
if (isPartOfRegion) {
for (int i = firstColumn; i <= lastColumn; i++) {
width += sheet.getColumnWidth(i);
}
for (int i = firstRow; i <= lastRow; i++) {
height += sheet.getRow(i).getHeight();
}
if (lastRow > firstRow) {
isPartOfRowsRegion = true;
}
} else {
width = sheet.getColumnWidth(columnIndex);
height += cell.getRow().getHeight();
}
map.put("isPartOfRowsRegion", isPartOfRowsRegion);
map.put("firstRow", firstRow);
map.put("lastRow", lastRow);
map.put("width", width);
map.put("height", height);
return map;
}
}
到此這篇關(guān)于Java導(dǎo)出Execl疑難點(diǎn)處理的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java導(dǎo)出Execl內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解如何將JAVA程序制作成可以直接執(zhí)行的exe文件
- java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行的方法詳解
- java 定時(shí)器線程池(ScheduledThreadPoolExecutor)的實(shí)現(xiàn)
- Java線程池ThreadPoolExecutor原理及使用實(shí)例
- Java使用ExecutorService來(lái)停止線程服務(wù)
- java.lang.Runtime.exec() Payload知識(shí)點(diǎn)詳解
- 把Java程序轉(zhuǎn)換成exe,可直接運(yùn)行的實(shí)現(xiàn)
相關(guān)文章
Java 設(shè)置Excel條件格式示例代碼(高亮條件值、應(yīng)用單元格值/公式/數(shù)據(jù)條等類型)
這篇文章主要介紹了Java 設(shè)置Excel條件格式示例代碼(高亮條件值、應(yīng)用單元格值/公式/數(shù)據(jù)條等類型),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
SpringBoot lombok(注解@Getter @Setter)詳解
通過(guò)使用Lombok庫(kù),SpringBoot應(yīng)用可以自動(dòng)化生成常用的方法如setter和getter,顯著降低了代碼冗余并提高了開發(fā)效率,Lombok的@Getter和@Setter注解用于自動(dòng)生成屬性的訪問(wèn)和修改方法,而@Data注解則提供了一個(gè)全面的解決方案2024-11-11
Spring中事務(wù)幾個(gè)常見的問(wèn)題解決
這篇文章主要介紹了Spring中事務(wù)幾個(gè)常見的問(wèn)題解決,事務(wù)這個(gè)概念是數(shù)據(jù)庫(kù)層面的,Spring只是基于數(shù)據(jù)庫(kù)中的事務(wù)進(jìn)行擴(kuò)展,以及提供了一些能讓程序員更新方便操作事務(wù)的方式2022-08-08
SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的流程步驟
優(yōu)雅停機(jī)(Graceful Shutdown) 是指在服務(wù)器需要關(guān)閉或重啟時(shí),能夠先處理完當(dāng)前正在進(jìn)行的請(qǐng)求,然后再停止服務(wù)的操作,本文給大家介紹了SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的流程步驟,需要的朋友可以參考下2024-03-03
Mybatis攔截器實(shí)現(xiàn)公共字段填充的示例代碼
本文介紹了使用Spring Boot和MyBatis實(shí)現(xiàn)公共字段的自動(dòng)填充功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
spring boot 配置freemarker及如何使用freemarker渲染頁(yè)面
springboot中自帶的頁(yè)面渲染工具為thymeleaf 還有freemarker這兩種模板引擎,本文重點(diǎn)給大家介紹spring boot 配置freemarker及如何使用freemarker渲染頁(yè)面,感興趣的朋友一起看看吧2023-10-10
springboot項(xiàng)目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問(wèn)題解決
這篇文章主要介紹了springboot項(xiàng)目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問(wèn)題解決,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-12-12
Java如何跳出當(dāng)前的多重嵌套循環(huán)的問(wèn)題
Java中的循環(huán)結(jié)構(gòu)包括for循環(huán)、while循環(huán)、do-while循環(huán)和增強(qiáng)型for循環(huán),每種循環(huán)都有其適用場(chǎng)景,在循環(huán)中,break、continue和return分別用于跳出循環(huán)、跳過(guò)當(dāng)前循環(huán)和結(jié)束當(dāng)前方法,對(duì)于多重嵌套循環(huán)2025-01-01

