前言
本文简单先容阿里的EasyExcel的复杂导出操作,包括自界说样式,根据数据合并单元格等。
点击查看EasyExcel官方文档
一、自界说拦截器
要实现复杂导出,靠现有的拦截器怕是不大够用,EasyExcel 已经有提供部分像是 自界说样式的策略HorizontalCellStyleStrategy
通过源码,我们不难发现其原理正是实现了拦截器接口,使用了afterCellDispose方法,在数据写入单元格后会调用该方法,因此,需要举行复杂操作,我们需要自界说拦截器,在afterCellDispose方法举行逻辑处置惩罚,此中我们可以通过context参数获取到表,行,列及单元格数据等信息:
二、自界说操作
1.自界说颜色
由于WriteCellStyle 及CellStyle接口的设置单元格配景颜色方法setFillForegroundColor不支持自界说颜色,我在网上找了半天,以及询问阿里自家ai助手通义得到的答案都是往里塞一个XSSFColor这样的答案,但这个方法传参是一个short类型的index呀,是预设好的颜色,内里也没有找到其他重载方法。(这里针对的是导出xlsx文件)
而真正可以自界说颜色的是XSSFCellStyle类,XSSFCellStyle实现CellStyle接口,并重载了该方法,于是我们只需要在workbook.createCellStyle()的时间将其强转为XSSFCellStyle:
- // 将背景设置成浅蓝色
- 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 + 鼠标点击接口,来查看接口的全部实现类(HSSF是针对xls的):
然而在我们自界说的拦截器中,操作当前单元格样式时会无法见效,这是由于在3.1.x版本后有一个FillStyleCellWriteHandler拦截器,他会把OriginCellStyle和WriteCellStyle合并,会已WriteCellStyle样式为主,他的order是50000,而我们自界说的拦截器默认是0,因此我们修改的样式会被覆盖。
解决方法很简单,我们可以在我们的自界说拦截器重写order方法,将其值设置大于50000即可
- @Override
- public int order() {
- return 50001;
- }
复制代码 如果你没有使用自界说拦截器(如HorizontalCellStyleStrategy )以及没有设置WriteCellStyle 样式,则还可以将ignoreFillStyle置为true,
- @Override
- public void afterCellDispose(CellWriteHandlerContext context) {
- context.setIgnoreFillStyle(true);
- // 做其他样式操作
- }
-
复制代码 2.合并单元格
- ```java
- @Override
- public void afterCellDispose(CellWriteHandlerContext context) {
- // 判断当前为表头,不执行操作
- if (isHead) {
- log.info("\r\n当前为表头, 不执行操作");
- return;
- }
- // 获取当前单元格
- context.getCell()
- // 当前 Sheet
- Sheet sheet = cell.getSheet();
- // 当前单元格所在行索引
- int rowIndexCurr = cell.getRowIndex();
- // 当前单元格所在列索引
- int columnIndex = cell.getColumnIndex();
- // 当前单元格所在行的上一行索引
- int rowIndexPrev = rowIndexCurr - 1;
- // 当前单元格所在行的 Row 对象
- Row rowCurr = cell.getRow();
- // 当前单元格所在行的上一行 Row 对象
- Row rowPrev = sheet.getRow(rowIndexPrev);
- // 当前单元格的上一行同列单元格
- Cell cellPrev = rowPrev.getCell(columnIndex);
- // 合并同列不同行的相邻两个单元格
- sheet.addMergedRegion(new CellRangeAddress(rowIndexPrev, rowIndexCurr,columnIndex, columnIndex));
-
- }
-
复制代码 需要注意的是,如果要合并的单元格已经被其他单元格合并过,则不能直接使用这个合并方法,需要先解除合并,再举行组合合并:
- // 从 Sheet 中,获取所有合并区域
- List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
- // 判断是否合并过
- boolean merged = false;
- // 遍历合并区域集合
- 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);
- // 设置范围最后一行,为当前行
- cellAddresses.setLastRow(rowIndexCurr);
- // 重新进行合并
- sheet.addMergedRegion(cellAddresses);
- merged = true;
- break;
- }
- }
- // merged=false,表示当前单元格为第一次合并
- if (!merged) {
- CellRangeAddress cellAddresses = new CellRangeAddress(rowIndexPrev, rowIndexCurr, cell.getColumnIndex(), cell.getColumnIndex());
- sheet.addMergedRegion(cellAddresses);
- }
复制代码 三、复杂操作示例
自界说拦截器代码如下(示例):
1.实体(使用了注解式样式):
- 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;
- /**
- * 账单实体类
- * @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({"服务商"})
- private String stockCreatorMchid;
- @ExcelProperty({"地区"})
- 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
- * 账单导出样式处理
- */
- public class CustomCellWriteHandler implements CellWriteHandler {
- /**
- * 自定义颜色
- */
- private final java.awt.Color color;
- /**
- * 自定义颜色样式
- */
- private CellStyle colorfulCellStyle;
- /**
- * 自定义特殊金额样式
- */
- private CellStyle specialCellStyle;
- /**
- * 头样式
- */
- private final WriteCellStyle headWriteCellStyle;
- /**
- * 内容样式
- */
- private final WriteCellStyle contentWriteCellStyle;
- /**
- * 头样式(可自定义颜色)
- */
- private CellStyle headCellStyle;
- /**
- * 内容样式(可自定义颜色)
- */
- private CellStyle contentCellStyle;
- public CustomCellWriteHandler(WriteCellStyle headWriteCellStyle,WriteCellStyle contentWriteCellStyle, java.awt.Color color) {
- this.headWriteCellStyle = headWriteCellStyle;
- this.contentWriteCellStyle = contentWriteCellStyle;
- this.color = color;
- }
- @Override
- public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
- // 在创建单元格之前的操作(如果需要)
- 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);
- }
- /*
- * 创建自定义颜色样式
- */
- private CellStyle createColorfulCellStyle(Workbook workbook) {
- XSSFColor customColor = new XSSFColor(color, null);
- XSSFCellStyle style = (XSSFCellStyle)workbook.createCellStyle();
- // 设置自定义颜色
- style.setFillForegroundColor(customColor);
- style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
- // 设置边框
- style.setBorderTop(BorderStyle.THIN);
- style.setBorderBottom(BorderStyle.THIN);
- style.setBorderLeft(BorderStyle.THIN);
- style.setBorderRight(BorderStyle.THIN);
- // 设置垂直对齐方式
- style.setVerticalAlignment(VerticalAlignment.CENTER);
- // 设置水平对齐方式
- style.setAlignment(HorizontalAlignment.CENTER);
- return style;
- }
- /*
- * 创建自定义特殊金额样式
- */
- 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 写入后处理
- *
- * @param writeSheetHolder
- * @param writeTableHolder
- * @param cellDataList
- * @param cell 当前 Cell
- * @param head
- * @param relativeRowIndex 表格内容行索引,从除表头的第一行开始,索引为0
- * @param isHead 是否是表头,true表头,false非表头
- */
- @Override
- public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
- List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
- // 当前 Sheet
- Sheet sheet = cell.getSheet();
- // 判断当前为表头,执行对应样式操作
- if (isHead) {
- cell.setCellStyle(headCellStyle);
- } else {
- cell.setCellStyle(contentCellStyle);
- }
- // 判断当前为表头,不执行操作
- if (isHead || relativeRowIndex == 0) {
- return;
- }
- int columnIndex = cell.getColumnIndex();
- // 当前 Cell 所在行索引
- int rowIndexCurr = cell.getRowIndex();
- // 当前 Cell 所在行的上一行索引
- int rowIndexPrev = rowIndexCurr - 1;
- // 当前 Cell 所在行的 Row 对象
- Row rowCurr = cell.getRow();
- // 当前 Cell 所在行的上一行 Row 对象
- Row rowPrev = sheet.getRow(rowIndexPrev);
- // 当前单元格的上一行同列单元格
- Cell cellPrev = rowPrev.getCell(columnIndex);
- // 当前单元格的值
- Object cellValueCurr = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
- if (columnIndex == 3 && cellValueCurr != null && (double)cellValueCurr > 200) {
- // 判断金额大于200就设置特定颜色并加粗,并将上一列同一行的数据也设置特定颜色
- CellStyle cellStyle = createSpecialCellStyle(sheet.getWorkbook());
- cell.setCellStyle(cellStyle);
- // 当前单元格的同行上一列单元格
- Cell cellPreC = rowCurr.getCell(columnIndex - 1);
- cellPreC.setCellStyle(colorfulCellStyle);
- }
- // 上面单元格的值
- Object cellValuePrev = cellPrev.getCellType() == CellType.STRING ? cellPrev.getStringCellValue() : cellPrev.getNumericCellValue();
- /*
- * 只判断前两列相同行数据
- */
- if (columnIndex != 0 && columnIndex != 1) {
- return;
- }
- // 判断当前单元格与上面单元格是否相等,不相等不执行操作
- if (!cellValueCurr.equals(cellValuePrev)) {
- return;
- }
- /*
- * 当第一列上下两个单元格不一样时,说明不是一个账期数据
- */
- if (!rowPrev.getCell(0).getStringCellValue().equals(rowCurr.getCell(0).getStringCellValue())) {
- return;
- }
- // 从 Sheet 中,获取所有合并区域
- List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
- // 是否合并过
- boolean merged = false;
- // 遍历合并区域集合
- 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);
- // 设置范围最后一行,为当前行
- cellAddresses.setLastRow(rowIndexCurr);
- // 重新添加到 Sheet 中
- sheet.addMergedRegion(cellAddresses);
- // 已完成合并
- merged = true;
- break;
- }
- }
- // merged=false,表示当前单元格为第一次合并
- if (!merged) {
- CellRangeAddress cellAddresses = new CellRangeAddress(rowIndexPrev, rowIndexCurr, columnIndex, columnIndex);
- sheet.addMergedRegion(cellAddresses);
- }
- }
- /**
- * 获取当前处理器优先级
- */
- @Override
- public int order() {
- return 50001;
- }
- }
复制代码 3.代码
- public static void main(String[] args) {
- String fileName = "D:\\temp\\账单.xlsx";
- // 设置 Cell 样式
- WriteCellStyle writeCellStyle = new WriteCellStyle();
- // 设置垂直对齐方式
- writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
- // 设置水平对齐方式
- writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
- // 设置边框
- 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","服务商" + i%2,"地区" + 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则可以继续
- // WriteSheet writeSheet2 = EasyExcel.writerSheet(1, "第二个sheet")
- excelWriter.finish();
- }
复制代码 4.终极效果
待续…
参考文章:
easyexcel 3.1.0+,设置RBG配景颜色
EasyExcel导出多sheet并设置单元格样式
EasyExcel的CellWriteHandler注入CellStyle不见效
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |