参考网上的链接:
EasyExcel按模板导出与下载(自定义合并单元格) https://blog.csdn.net/weixin_44511845/article/details/120290264 EasyExcel(根据条件动态合并单元格的重复数据)) https://blog.csdn.net/Violet_201903027/article/details/105724907
编写模板导出时,某一列单元格合并功能
上面的资料使用的EasyExcel版本是:2.1.7
我使用的版本是:2.2.0-beta2
使用资料代码进行模板导出同时,动态单元格合并
github官方提供了 LoopMergeStrategy 合并策略 在模板导出不生效。因为LoopMergeStrategy extends AbstractRowWriteHandler ,使用 afterRowDispose() 行操作完成后执行合并操作。
但是在模板导出过程中afterRowDispose() 方法没有被触发。
合并策略类:ExcelFillCellMergeStrategy
1 public class ExcelFillCellMergeStrategy implements CellWriteHandler { 2 3 /** 需要进行单元格合并的列数组 **/ 4 private int[] mergeColumnIndex; 5 /** 单元格合并从第几行开始 **/ 6 private int mergeRowIndex; 7 8 public ExcelFillCellMergeStrategy() {} 9 10 public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) { 11 this.mergeRowIndex = mergeRowIndex; 12 this.mergeColumnIndex = mergeColumnIndex; 13 } 14 。。。 15 。。。 16 17 @Override 18 public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, 19 List<CellData> list, Cell cell, Head head, Integer integer, Boolean isHead) { 20 int curRowIndex = cell.getRowIndex(); 21 int curColIndex = cell.getColumnIndex(); 22 if (curRowIndex > mergeRowIndex) { 23 for (int i = 0; i < mergeColumnIndex.length; i++) { 24 if (curColIndex == mergeColumnIndex[i]) { 25 mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex); 26 break; 27 } 28 } 29 } 30 } 31 32 /** 33 * 当前单元格向上合并 34 * 35 * @param writeSheetHolder 36 * @param cell 37 * 当前单元格 38 * @param curRowIndex 39 * 当前行 40 * @param curColIndex 41 * 当前列 42 */ 43 private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) { 44 Object curData = 45 cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue(); 46 Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex); 47 Object preData = 48 preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); 49 // 将当前单元格数据与上一个单元格数据比较 50 Boolean dataBool = preData.equals(curData); 51 if (dataBool) { 52 Sheet sheet = writeSheetHolder.getSheet(); 53 List<CellRangeAddress> mergeRegions = sheet.getMergedRegions(); 54 boolean isMerged = false; 55 for (int i = 0; i < mergeRegions.size() && !isMerged; i++) { 56 CellRangeAddress cellRangeAddr = mergeRegions.get(i); 57 // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 58 if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) { 59 sheet.removeMergedRegion(i); 60 cellRangeAddr.setLastRow(curRowIndex); 61 sheet.addMergedRegion(cellRangeAddr); 62 isMerged = true; 63 } 64 } 65 // 若上一个单元格未被合并,则新增合并单元 66 if (!isMerged) { 67 CellRangeAddress cellRangeAddress = 68 new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex); 69 sheet.addMergedRegion(cellRangeAddress); 70 } 71 } 72 } 73 }
测试代码
@Test public void fillTest1() throws IOException { // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 // 填充list 的时候还要注意 模板中{.} 多了个点 表示list String templatePath = EasyexcelAnnotationFillTest.class.getResource("/").getPath(); templatePath = templatePath + "fillTemplate.xlsx"; OutputStream outputStream = new FileOutputStream("E:/测试填充数据.xlsx"); // 填充列表数据 List<FillWithAnnotationData> listData = getFillListData(); // 第一列进行单元格合并 int[] mergeColumeIndex = {0}; // 从第4行开始合并 int mergeRowIndex = 3; ExcelWriter excelWriter = EasyExcelFactory.write(outputStream) .withTemplate(templatePath) //设置合并单元格策略 .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex)) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); excelWriter.fill(listData, writeSheet); excelWriter.finish(); outputStream.close(); }
public List<FillWithAnnotationData> getFillListData() {
List<FillWithAnnotationData> listData = new ArrayList<>();
for (int i=0; i < 10; i++) {
FillWithAnnotationData data = new FillWithAnnotationData();
data.setName("名称");
data.setMoney(new BigDecimal(1002.35+i).toPlainString());
data.setNumber(1002.35+i);
listData.add(data);
}
return listData;
}
public class FillWithAnnotationData {
private String name;
@NumberFormat(",##0.00")
@ExcelFillProperty(converter = StringNumberConverter.class)
private String money;
@NumberFormat(",##0.00")
@ExcelFillProperty(converter = DoubleStringConverter.class)
private Double number;
}
测试模板如下图
报错位置:ExcelFillCellMergeStrategy合并策略类的 mergeWithPrevRow()方法中
第46行位置
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
这一行代码会报空指针异常 java.lang.NullPointerException
原因:
debug发现,cell.getSheet() 行的下标第0到3的数据行,获取的是同一个 sheet 实例
当下标为4时,执行cell.getSheet()获取到的 sheet 实例不一样,而且里面的sheet存在的row数据,只有下标为4以后的
而下标0到3的行数据被存储到 存储sheet中。
writeSheetHolder.getCachedSheet()
所以改进合并策略类的合并方法:
当获取不到前一行数据时,查找缓存sheet中的行数据
/** * 当前单元格向上合并 * * @param writeSheetHolder * @param cell * 当前单元格 * @param curRowIndex * 当前行 * @param curColIndex * 当前列 */ private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) { Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue(); Row preRow = cell.getSheet().getRow(curRowIndex - 1); if (preRow == null) { // 当获取不到上一行数据时,使用缓存sheet中数据 preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1); } Cell preCell = preRow.getCell(curColIndex); Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); // 将当前单元格数据与上一个单元格数据比较 Boolean dataBool = preData.equals(curData); if (dataBool) { Sheet sheet = writeSheetHolder.getSheet(); List<CellRangeAddress> mergeRegions = sheet.getMergedRegions(); boolean isMerged = false; for (int i = 0; i < mergeRegions.size() && !isMerged; i++) { CellRangeAddress cellRangeAddr = mergeRegions.get(i); // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) { sheet.removeMergedRegion(i); cellRangeAddr.setLastRow(curRowIndex); sheet.addMergedRegion(cellRangeAddr); isMerged = true; } } // 若上一个单元格未被合并,则新增合并单元 if (!isMerged) { CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex); sheet.addMergedRegion(cellRangeAddress); } } }