EasyExcel按模板導出(動態合並單元格問題處理)


參考網上的鏈接:

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);
            }
        }
    }

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM