原创

解决 easypoi 循环指令合并单元格的问题

前言

使用 easypoi 循环指令时,发现下面3种情况循环会出现问题,导致excel格式错乱。

  1. 循环开始标识与结束标识之间存在合并单元格。
  2. 循环开始标识左侧存在合并单元格。
  3. 存在多个循环指令时。

如何解决

反编译下面这个类,在其基础上修改

cn.afterturn.easypoi.excel.export.template.ExcelExportOfTemplateUtil

循环开始标识与结束标识之间存在合并单元格,解决方法

修改 addListDataToExcel 方法

private void addListDataToExcel(Cell cell, Map<String, Object> map,String name) throws Exception {

    ......

    // 定义循环开始行号
    int startRowNum = cell.getRow().getRowNum();

    ......

    // 定义循环结束行号
    int endRowNum = 0;
    while (its.hasNext()) {
        Object t = its.next();
        row = createRow(rowIndex, cell.getSheet(), isCreate, rowspan);
        setForEeachRowCellValue(isCreate, row, cell.getColumnIndex(), t, columns, map, rowspan,
                colspan, mergedRegionHelper,startRowNum);
        rowIndex += rowspan;
        // 每次创建行后,重新给循环结束行号赋值
        endRowNum = row.getRowNum();
    }
    // 如果新创建行了
    // 合并循环左侧竖向单元格
    if (endRowNum != 0){
        Sheet sheet = cell.getRow().getSheet();
        int sheetMergeCount = sheet.getNumMergedRegions();
        int columnIndex = cell.getColumnIndex();
        for(int i = 0; i < sheetMergeCount; ++i) {
            CellRangeAddress ca = sheet.getMergedRegion(i);
            //int firstColumn = ca.getFirstColumn();
            int lastColumn = ca.getLastColumn();
            int firstRow = ca.getFirstRow();
            int lastRow = ca.getLastRow();
            for (int ii = 0;ii < columnIndex;ii++){
                if (firstRow <= startRowNum && lastRow > startRowNum && lastColumn == ii){
                    ca.setLastRow(lastRow + (endRowNum - startRowNum));
                }
            }
        }
    }
}

循环开始标识左侧存在合并单元格,解决方法

修改 addListDataToExcel 方法,将循环开始行号,传入 setForEeachRowCellValue 方法

private void addListDataToExcel(Cell cell, Map<String, Object> map,String name) throws Exception {

    ......
    // 定义循环开始行号
    int startRowNum = cell.getRow().getRowNum();

    ......

    // 处理当前行
    if (its.hasNext()) {
        Object t = its.next();
        setForEeachRowCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns, map,
                rowspan, colspan, mergedRegionHelper,startRowNum);
        rowIndex += rowspan - 1;
    }

    ......

    // 创建行
    while (its.hasNext()) {
        Object t = its.next();
        row = createRow(rowIndex, cell.getSheet(), isCreate, rowspan);
        setForEeachRowCellValue(isCreate, row, cell.getColumnIndex(), t, columns, map, rowspan,
                colspan, mergedRegionHelper,startRowNum);
        rowIndex += rowspan;
        endRowNum = row.getRowNum();
    }

    ......

}

修改 setForEeachRowCellValue 方法

// 添加参数:循环开始行号
private void setForEeachRowCellValue(boolean isCreate, Row row, int columnIndex, Object t,
                                         List<ExcelForEachParams> columns, Map<String, Object> map,
                                         int rowspan, int colspan,
                                         MergedRegionHelper mergedRegionHelper,int startRowNum) throws Exception {

    ......

                //合并对应单元格
                if ((params.getRowspan() != 1 || params.getColspan() != 1)
                        // 存在合并单元格时,这个判断出问题了,需要注释
                        //&& !mergedRegionHelper.isMergedRegion(row.getRowNum() + 1, ci)
                        // 将第二个参数改为:循环开始行号
                        // 原因:这个方法原先是判断当前行这一列,是否需要合并单元格
                        //             如果是新创建的行,这个方法恒定返回 false ,判断出现问题
                        //             所以需要改为:判断循环开始行这一列,是否需要合并单元格
                        && PoiCellUtil.isMergedRegion(row.getSheet(), startRowNum, ci)
                ) {
                    PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),
                            row.getRowNum() + params.getRowspan() - 1, ci,
                            ci + params.getColspan() - 1);
                }

    ......

}

存在多个循环指令时,解决方法

修改 addListDataToExcel 方法

private void addListDataToExcel(Cell cell, Map<String, Object> map,String name) throws Exception {
    // 每次利用foreach循环输出数据时,重新处理 mergedRegionHelper
    // 原因:如果存在多个循环,前面循环时,下面的模板指令所在单元格的行号会发生变化,此时 mergedRegionHelper 中的缓存没有发生相应变化,需要重新获取一下 mergedRegionHelper
    mergedRegionHelper = new MergedRegionHelper(cell.getSheet());

    ......

}

注意

  1. 反编译 cn.afterturn.easypoi.excel.export.template.ExcelExportOfTemplateUtil 这个类后,爆红的代码,需要修改。
  2. setForEeachRowCellValue 方法添加参数:循环开始行号后,所有引用这个方法的地方,都需要修改。
正文到此结束