easyexcel实现相同内容的上下行合并

需求:同一列上下行内容相同的情况下进行合并,有一个前提要求是某一列的值相同情况下,再去判断要合并的列是否值相同。

比如下面的核心代码中前提是第19列的值相同的前提下,再去判断要合并的上下行是否相同,如果相同就合并,不相同就不合并。这里有个坑,可以继续往下看。

实现: 

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.Data;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;
import java.util.Objects;

@Data
public class ExcelFillCellMergeStrategy implements CellWriteHandler {

    //需要合并的列
    private int[] mergeColumnIndex;

    //需要从第二行开始,列头第二行
    private int mergeRowIndex;

    public ExcelFillCellMergeStrategy() {
    }

    public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }


    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List> cellDataList,
                                 Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();

        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < mergeColumnIndex.length; i++) {
                if (curColIndex == mergeColumnIndex[i] && curColIndex == 19) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                } else if (curColIndex == mergeColumnIndex[i] && curColIndex != 19) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    /**
     * 当前单元格向上合并
     *
     * @param writeSheetHolder
     * @param cell             当前单元格
     * @param curRowIndex      当前行
     * @param curColIndex      当前列
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell,
                                  int curRowIndex, int curColIndex) {
        //获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();

        //当前行第19列的值
        Cell businessCodeCell = cell.getSheet().getRow(curRowIndex).getCell(19);
        //上一行第19列的值
        Cell prebusinessCodeCell = cell.getSheet().getRow(curRowIndex - 1).getCell(19);
        String businessCodeData = businessCodeCell.getStringCellValue();
        String preBusinessCodeData = prebusinessCodeCell.getStringCellValue();



        // 我是因为有一些数据是空串,不进行合并,所以做了特殊处理的。可以根据自己的需求进行修改
        // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
        if (Objects.equals(cell.getCellType(), CellType.STRING) && Objects.equals(preCell.getCellType(), CellType.STRING)
                && curData.equals(preData)) {
            if (curColIndex != 19 && Objects.equals(businessCodeData, preBusinessCodeData)) {
                addmergeRegion(writeSheetHolder, curRowIndex, curColIndex);
            } else if (curColIndex == 19) {
                addmergeRegion(writeSheetHolder, curRowIndex, curColIndex);
            }
        } else if (Objects.equals(cell.getCellType(), CellType.NUMERIC) && Objects.equals(preCell.getCellType(), CellType.NUMERIC)
                && curData.equals(preData)
                && Objects.nonNull(businessCodeData) && Objects.nonNull(preBusinessCodeData)
                && Objects.equals(businessCodeData, preBusinessCodeData)) {
            addmergeRegion(writeSheetHolder, curRowIndex, curColIndex);
        }
    }

    private void addmergeRegion(WriteSheetHolder writeSheetHolder, int curRowIndex, int curColIndex) {
        Sheet sheet = writeSheetHolder.getSheet();
        List 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);
        }
    }
}
public void export(HttpServletResponse response, InvestmentAndIncomeInfoDTO investmentAndIncomeInfoDTO) throws Exception {
        //需要合并的列
        int[] mergeColumeIndex = {19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40};
        //需要从第二行开始,列头第二行
        int mergeRowIndex = 2;

        String date = DateTimeUtil.date();
        List exportData = getExportData();
        String fileName = URLEncoder.encode("测试合并" + date + ".xlsx", "UTF-8");
        ExcelDataUtil.setResponseHeader(response, fileName);
        EasyExcel.write(response.getOutputStream(), InvestmentAndIncomeInfoDTO.class)
                .excelType(ExcelTypeEnum.XLSX)
                  //内容居中
                .registerWriteHandler(horizontalCellStyleStrategyBuilder())
                .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex))
                .sheet("测试合并")
                .doWrite((exportData));
    }

让合并的内容居中并且加上框线显示的代码。

 public HorizontalCellStyleStrategy horizontalCellStyleStrategyBuilder() {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 单元格边框类型
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        // 单元格边框颜色
        headWriteCellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
        headWriteCellStyle.setRightBorderColor(IndexedColors.BLACK.index);
        headWriteCellStyle.setTopBorderColor(IndexedColors.BLACK.index);
        headWriteCellStyle.setBottomBorderColor(IndexedColors.BLACK.index);

        //内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 单元格边框类型
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        // 单元格边框颜色
        contentWriteCellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
        contentWriteCellStyle.setRightBorderColor(IndexedColors.BLACK.index);
        contentWriteCellStyle.setTopBorderColor(IndexedColors.BLACK.index);
        contentWriteCellStyle.setBottomBorderColor(IndexedColors.BLACK.index);

        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

上述代码自己可以添加一些样例数据。进行测试。很完美的实现了。如果数据量多的情况下,执行效率是不高的。有什么好的办法欢迎评论留言,一起学习。

说一下这次的坑,此次的需求是在第19列(第19列的值不会为空)值相同的情况下再进行合并,这个时候我又想合并前18列,于是我修改了要合并的列集合。

int[] mergeColumeIndex = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40};

运行,报错,空指针!!!非常好,有问题才有进步嘛。

跟代码看看,发现在获取某行第19列值的情况下获取不到。

一路看下来,发现只有在进行合并前18列的时候才会发生这个问题,有点眉目了哈,在合并前18列时,是获取不到当前合并行合并列后的值的!!!

这应该是easyexcel 的特殊机制吧。原生的poi 我没试。有小伙伴试的话欢迎评论留言。

好了,前面的不合并了。就这样吧!!!

你可能感兴趣的:(java)