參考網上的鏈接:
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); } } }