網(wǎng)站建設(shè)制作設(shè)計(jì)營(yíng)銷(xiāo) 廣州網(wǎng)站頁(yè)面怎么優(yōu)化
文章目錄
- 前言
- 一、自定義攔截器
- 二、自定義操作
- 1.自定義顏色
- 2.合并單元格
- 三、復(fù)雜操作示例
- 1.實(shí)體(使用了注解式樣式):
- 2.自定義攔截器
- 3.代碼
- 4.最終效果
前言
本文簡(jiǎn)單介紹阿里的EasyExcel的復(fù)雜導(dǎo)出操作,包括自定義樣式,根據(jù)數(shù)據(jù)合并單元格等。
點(diǎn)擊查看EasyExcel官方文檔
一、自定義攔截器
要實(shí)現(xiàn)復(fù)雜導(dǎo)出,靠現(xiàn)有的攔截器怕是不大夠用,EasyExcel 已經(jīng)有提供部分像是 自定義樣式的策略HorizontalCellStyleStrategy
通過(guò)源碼,我們不難發(fā)現(xiàn)其原理正是實(shí)現(xiàn)了攔截器接口,使用了afterCellDispose方法,在數(shù)據(jù)寫(xiě)入單元格后會(huì)調(diào)用該方法,因此,需要進(jìn)行復(fù)雜操作,我們需要自定義攔截器,在afterCellDispose方法進(jìn)行邏輯處理,其中我們可以通過(guò)context參數(shù)獲取到表,行,列及單元格數(shù)據(jù)等信息:
二、自定義操作
1.自定義顏色
由于WriteCellStyle 及CellStyle接口的設(shè)置單元格背景顏色方法setFillForegroundColor不支持自定義顏色,我在網(wǎng)上找了半天,以及詢(xún)問(wèn)阿里自家ai助手通義得到的答案都是往里塞一個(gè)XSSFColor這樣的答案,但這個(gè)方法傳參是一個(gè)short類(lèi)型的index呀,是預(yù)設(shè)好的顏色,里面也沒(méi)有找到其他重載方法。(這里針對(duì)的是導(dǎo)出xlsx文件)
而真正可以自定義顏色的是XSSFCellStyle類(lèi),XSSFCellStyle實(shí)現(xiàn)CellStyle接口,并重載了該方法,于是我們只需要在workbook.createCellStyle()的時(shí)候?qū)⑵鋸?qiáng)轉(zhuǎn)為XSSFCellStyle:
// 將背景設(shè)置成淺藍(lán)色
XSSFColor customColor = new XSSFColor(new java.awt.Color(181, 198, 234), null);
XSSFCellStyle style = (XSSFCellStyle)workbook.createCellStyle();
style.setFillForegroundColor(customColor);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);
在idea我們可以使用 ctrl + alt + 鼠標(biāo)點(diǎn)擊接口,來(lái)查看接口的所有實(shí)現(xiàn)類(lèi)(HSSF是針對(duì)xls的):
然而在我們自定義的攔截器中,操作當(dāng)前單元格樣式時(shí)會(huì)無(wú)法生效,這是因?yàn)樵?.1.x版本后有一個(gè)FillStyleCellWriteHandler攔截器,他會(huì)把OriginCellStyle和WriteCellStyle合并,會(huì)已WriteCellStyle樣式為主,他的order是50000,而我們自定義的攔截器默認(rèn)是0,因此我們修改的樣式會(huì)被覆蓋。
解決方法很簡(jiǎn)單,我們可以在我們的自定義攔截器重寫(xiě)order方法,將其值設(shè)置大于50000即可
@Overridepublic int order() {return 50001;}
如果你沒(méi)有使用自定義攔截器(如HorizontalCellStyleStrategy )以及沒(méi)有設(shè)置WriteCellStyle 樣式,則還可以將ignoreFillStyle置為true,
@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {context.setIgnoreFillStyle(true);// 做其他樣式操作}
2.合并單元格
```java@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {// 判斷當(dāng)前為表頭,不執(zhí)行操作if (isHead) {log.info("\r\n當(dāng)前為表頭, 不執(zhí)行操作");return;}// 獲取當(dāng)前單元格context.getCell()// 當(dāng)前 SheetSheet sheet = cell.getSheet();// 當(dāng)前單元格所在行索引int rowIndexCurr = cell.getRowIndex();// 當(dāng)前單元格所在列索引int columnIndex = cell.getColumnIndex();// 當(dāng)前單元格所在行的上一行索引int rowIndexPrev = rowIndexCurr - 1;// 當(dāng)前單元格所在行的 Row 對(duì)象Row rowCurr = cell.getRow();// 當(dāng)前單元格所在行的上一行 Row 對(duì)象Row rowPrev = sheet.getRow(rowIndexPrev);// 當(dāng)前單元格的上一行同列單元格Cell cellPrev = rowPrev.getCell(columnIndex);// 合并同列不同行的相鄰兩個(gè)單元格sheet.addMergedRegion(new CellRangeAddress(rowIndexPrev, rowIndexCurr,columnIndex, columnIndex));}
需要注意的是,如果要合并的單元格已經(jīng)被其他單元格合并過(guò),則不能直接使用這個(gè)合并方法,需要先解除合并,再進(jìn)行組合合并:
// 從 Sheet 中,獲取所有合并區(qū)域List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();// 判斷是否合并過(guò)boolean merged = false;// 遍歷合并區(qū)域集合for (int i = 0; i < mergedRegions.size(); i++) {CellRangeAddress cellAddresses = mergedRegions.get(i);// 判斷 cellAddress 的范圍是否是從 rowIndexPrev 到 cell.getColumnIndex()if (cellAddresses.isInRange(rowIndexPrev, cell.getColumnIndex())) {// 解除合并sheet.removeMergedRegion(i);// 設(shè)置范圍最后一行,為當(dāng)前行cellAddresses.setLastRow(rowIndexCurr);// 重新進(jìn)行合并sheet.addMergedRegion(cellAddresses);merged = true;break;}}// merged=false,表示當(dāng)前單元格為第一次合并if (!merged) {CellRangeAddress cellAddresses = new CellRangeAddress(rowIndexPrev, rowIndexCurr, cell.getColumnIndex(), cell.getColumnIndex());sheet.addMergedRegion(cellAddresses);}
三、復(fù)雜操作示例
自定義攔截器代碼如下(示例):
1.實(shí)體(使用了注解式樣式):
package com.mhqs.demo.tool.easyExcel.entity;import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentFontStyle;
import com.alibaba.excel.annotation.write.style.HeadFontStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import lombok.EqualsAndHashCode;import java.math.BigDecimal;/*** 賬單實(shí)體類(lèi)* @author 棉花* */
@Data
@EqualsAndHashCode(callSuper = false)
@HeadFontStyle(fontHeightInPoints = 10)
@HeadRowHeight(27)
@ColumnWidth(13)
@ContentFontStyle(fontName = "宋體",fontHeightInPoints = 11)
public class DemoEntity extends EasyExcelEntity {@ExcelProperty({"賬期"})private String settlePeriod;@ExcelProperty({"服務(wù)商"})private String stockCreatorMchid;@ExcelProperty({"地區(qū)"})private String place;@ExcelProperty({"金額(元)"})private BigDecimal consumeAmount;public DemoEntity(String settlePeriod, String stockCreatorMchid,String place, BigDecimal consumeAmount){this.settlePeriod = settlePeriod;this.stockCreatorMchid = stockCreatorMchid;this.place = place;this.consumeAmount = consumeAmount;}}
2.自定義攔截器
package com.mhqs.demo.tool.easyExcel.handler;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.StyleUtil;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.mhqs.demo.tool.easyExcel.entity.DemoEntity;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;import java.math.BigDecimal;
import java.util.*;/*** @author bcb* 賬單導(dǎo)出樣式處理*/
public class CustomCellWriteHandler implements CellWriteHandler {/*** 自定義顏色*/private final java.awt.Color color;/*** 自定義顏色樣式*/private CellStyle colorfulCellStyle;/*** 自定義特殊金額樣式*/private CellStyle specialCellStyle;/*** 頭樣式*/private final WriteCellStyle headWriteCellStyle;/*** 內(nèi)容樣式*/private final WriteCellStyle contentWriteCellStyle;/*** 頭樣式(可自定義顏色)*/private CellStyle headCellStyle;/*** 內(nèi)容樣式(可自定義顏色)*/private CellStyle contentCellStyle;public CustomCellWriteHandler(WriteCellStyle headWriteCellStyle,WriteCellStyle contentWriteCellStyle, java.awt.Color color) {this.headWriteCellStyle = headWriteCellStyle;this.contentWriteCellStyle = contentWriteCellStyle;this.color = color;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {// 在創(chuàng)建單元格之前的操作(如果需要)Workbook workbook = writeSheetHolder.getSheet().getWorkbook();if (colorfulCellStyle == null) {colorfulCellStyle = createColorfulCellStyle(workbook);}// 合并樣式(以WriteCellStyle為主)headCellStyle = StyleUtil.buildCellStyle(workbook, colorfulCellStyle, headWriteCellStyle);contentCellStyle = StyleUtil.buildCellStyle(workbook, workbook.createCellStyle(), contentWriteCellStyle);}/** 創(chuàng)建自定義顏色樣式*/private CellStyle createColorfulCellStyle(Workbook workbook) {XSSFColor customColor = new XSSFColor(color, null);XSSFCellStyle style = (XSSFCellStyle)workbook.createCellStyle();// 設(shè)置自定義顏色style.setFillForegroundColor(customColor);style.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 設(shè)置邊框style.setBorderTop(BorderStyle.THIN);style.setBorderBottom(BorderStyle.THIN);style.setBorderLeft(BorderStyle.THIN);style.setBorderRight(BorderStyle.THIN);// 設(shè)置垂直對(duì)齊方式style.setVerticalAlignment(VerticalAlignment.CENTER);// 設(shè)置水平對(duì)齊方式style.setAlignment(HorizontalAlignment.CENTER);return style;}/** 創(chuàng)建自定義特殊金額樣式*/private CellStyle createSpecialCellStyle(Workbook workbook) {if (specialCellStyle == null) {XSSFCellStyle style = (XSSFCellStyle)createColorfulCellStyle(workbook);Font font = workbook.createFont();// 字體加粗font.setBold(true);style.setFont(font);specialCellStyle = style;}return specialCellStyle;}/*** 在 Cell 寫(xiě)入后處理** @param writeSheetHolder* @param writeTableHolder* @param cellDataList* @param cell 當(dāng)前 Cell* @param head* @param relativeRowIndex 表格內(nèi)容行索引,從除表頭的第一行開(kāi)始,索引為0* @param isHead 是否是表頭,true表頭,false非表頭*/@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {// 當(dāng)前 SheetSheet sheet = cell.getSheet();// 判斷當(dāng)前為表頭,執(zhí)行對(duì)應(yīng)樣式操作if (isHead) {cell.setCellStyle(headCellStyle);} else {cell.setCellStyle(contentCellStyle);}// 判斷當(dāng)前為表頭,不執(zhí)行操作if (isHead || relativeRowIndex == 0) {return;}int columnIndex = cell.getColumnIndex();// 當(dāng)前 Cell 所在行索引int rowIndexCurr = cell.getRowIndex();// 當(dāng)前 Cell 所在行的上一行索引int rowIndexPrev = rowIndexCurr - 1;// 當(dāng)前 Cell 所在行的 Row 對(duì)象Row rowCurr = cell.getRow();// 當(dāng)前 Cell 所在行的上一行 Row 對(duì)象Row rowPrev = sheet.getRow(rowIndexPrev);// 當(dāng)前單元格的上一行同列單元格Cell cellPrev = rowPrev.getCell(columnIndex);// 當(dāng)前單元格的值Object cellValueCurr = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();if (columnIndex == 3 && cellValueCurr != null && (double)cellValueCurr > 200) {// 判斷金額大于200就設(shè)置特定顏色并加粗,并將上一列同一行的數(shù)據(jù)也設(shè)置特定顏色CellStyle cellStyle = createSpecialCellStyle(sheet.getWorkbook());cell.setCellStyle(cellStyle);// 當(dāng)前單元格的同行上一列單元格Cell cellPreC = rowCurr.getCell(columnIndex - 1);cellPreC.setCellStyle(colorfulCellStyle);}// 上面單元格的值Object cellValuePrev = cellPrev.getCellType() == CellType.STRING ? cellPrev.getStringCellValue() : cellPrev.getNumericCellValue();/** 只判斷前兩列相同行數(shù)據(jù)*/if (columnIndex != 0 && columnIndex != 1) {return;}// 判斷當(dāng)前單元格與上面單元格是否相等,不相等不執(zhí)行操作if (!cellValueCurr.equals(cellValuePrev)) {return;}/** 當(dāng)?shù)谝涣猩舷聝蓚€(gè)單元格不一樣時(shí),說(shuō)明不是一個(gè)賬期數(shù)據(jù)*/if (!rowPrev.getCell(0).getStringCellValue().equals(rowCurr.getCell(0).getStringCellValue())) {return;}// 從 Sheet 中,獲取所有合并區(qū)域List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();// 是否合并過(guò)boolean merged = false;// 遍歷合并區(qū)域集合for (int i = 0; i < mergedRegions.size(); i++) {CellRangeAddress cellAddresses = mergedRegions.get(i);//判斷 cellAddress 的范圍是否是從 rowIndexPrev 到 cell.getColumnIndex()if (cellAddresses.isInRange(rowIndexPrev, columnIndex)) {// 從集合中移除sheet.removeMergedRegion(i);// 設(shè)置范圍最后一行,為當(dāng)前行cellAddresses.setLastRow(rowIndexCurr);// 重新添加到 Sheet 中sheet.addMergedRegion(cellAddresses);// 已完成合并merged = true;break;}}// merged=false,表示當(dāng)前單元格為第一次合并if (!merged) {CellRangeAddress cellAddresses = new CellRangeAddress(rowIndexPrev, rowIndexCurr, columnIndex, columnIndex);sheet.addMergedRegion(cellAddresses);}}/*** 獲取當(dāng)前處理器優(yōu)先級(jí)*/@Overridepublic int order() {return 50001;}}
3.代碼
public static void main(String[] args) {String fileName = "D:\\temp\\賬單.xlsx";// 設(shè)置 Cell 樣式WriteCellStyle writeCellStyle = new WriteCellStyle();// 設(shè)置垂直對(duì)齊方式writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 設(shè)置水平對(duì)齊方式writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 設(shè)置邊框writeCellStyle.setBorderTop(BorderStyle.THIN);writeCellStyle.setBorderBottom(BorderStyle.THIN);writeCellStyle.setBorderLeft(BorderStyle.THIN);writeCellStyle.setBorderRight(BorderStyle.THIN);// 自定義顏色java.awt.Color color = new java.awt.Color(181, 198, 234);List<DemoEntity> dataList = new ArrayList<>();for (int i = 0; i < 5; i++) {dataList.add(new DemoEntity("202301","服務(wù)商" + i%2,"地區(qū)" + i,new BigDecimal(i * 100)));}dataList.sort(Comparator.comparing(DemoEntity::getSettlePeriod).thenComparing(DemoEntity::getStockCreatorMchid));ExcelWriter excelWriter = EasyExcel.write(fileName, DemoEntity.class).build();WriteSheet writeSheet = EasyExcel.writerSheet(0, "賬單").registerWriteHandler(new CustomCellWriteHandler(null,writeCellStyle,color)).build();excelWriter.write(dataList, writeSheet);// 需要多sheet則可以繼續(xù)// WriteSheet writeSheet2 = EasyExcel.writerSheet(1, "第二個(gè)sheet")excelWriter.finish();}
4.最終效果
待續(xù)…
參考文章:
easyexcel 3.1.0+,設(shè)置RBG背景顏色
EasyExcel導(dǎo)出多sheet并設(shè)置單元格樣式
EasyExcel的CellWriteHandler注入CellStyle不生效