使用EasyExcel 导出复杂的合并单元格

引入pom.xml

java 复制代码
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
java 复制代码
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.HashMap;
import java.util.Map;

public class CustomMergeStrategy extends AbstractMergeStrategy {
    private final int[] mergeColumns;  // 需要合并的列索引
    private final int startRow;        // 开始合并的行号
    private Map<Integer, MergeRange> lastValueMap = new HashMap<>();

    public CustomMergeStrategy(int[] mergeColumns, int startRow) {
        this.mergeColumns = mergeColumns;
        this.startRow = startRow;
    }

    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        int currentRow = cell.getRowIndex();
        if (currentRow <= startRow) return;

        for (int col : mergeColumns) {
            if (cell.getColumnIndex() == col) {
                String currentValue = cell.getStringCellValue();
                MergeRange lastRange = lastValueMap.get(col);

                if (lastRange != null && lastRange.value.equals(currentValue)) {
                    // 合并到上一个范围
                    sheet.addMergedRegion(new CellRangeAddress(
                            lastRange.startRow, currentRow, col, col
                    ));
                    lastValueMap.put(col, new MergeRange(
                            lastRange.startRow, currentValue
                    ));
                } else {
                    // 新值范围开始
                    lastValueMap.put(col, new MergeRange(
                            currentRow, currentValue
                    ));
                }
                break;
            }
        }
    }

    private static class MergeRange {
        int startRow;
        String value;

        MergeRange(int startRow, String value) {
            this.startRow = startRow;
            this.value = value;
        }
    }
}

完成列头合并

复制代码
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

public class ComplexHeadStrategy implements CellWriteHandler {

    public void afterCellDispose(WriteSheetHolder writeSheetHolder,
                                 WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {

        if (head.getColumnIndex() == 0 || head.getColumnIndex() == 4) { // 第一列和第五列
            Sheet sheet = writeSheetHolder.getSheet();
            if (cell.getRowIndex() == 0) { // 首行
                // 合并"引导车误差分析"标题
                sheet.addMergedRegion(new CellRangeAddress(
                        0, 0, 0, 1 // 合并前两列
                ));
                sheet.addMergedRegion(new CellRangeAddress(
                        0, 0, 4, 5 // 合并最后两列
                ));
            }
        }
    }
}

Demo实现

复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.Data;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.util.ArrayList;
import java.util.List;

public class MergeCellExportDemo {
    public static void main(String[] args) {
        List<FlightData> dataList = new ArrayList<>();
        dataList.add(new FlightData("0.12", "0.5", "B-1234", "A01", "0.08", "0.3"));
        dataList.add(new FlightData("0.15", "0.6", "B-5678", "B02", "0.09", "0.4"));

        // 自定义合并策略(合并机号列)
        CustomMergeStrategy mergeStrategy = new CustomMergeStrategy(
                new int[]{2, 3}, 1 // 合并第3、4列(机号/机位)
        );


        // 设置表头样式
        WriteCellStyle headStyle = new WriteCellStyle();
        headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        WriteCellStyle contentStyle = new WriteCellStyle();
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        HorizontalCellStyleStrategy styleStrategy =
                new HorizontalCellStyleStrategy(headStyle, contentStyle);

        EasyExcel.write("flight_report.xlsx")
                .head(FlightData.class)
                .registerWriteHandler(new ComplexHeadStrategy()) // 复杂表头处理
                .registerWriteHandler(mergeStrategy)
                .registerWriteHandler(styleStrategy)
                .sheet("航班数据")
                .doWrite(dataList);
    }

    // 数据模型类(对应图片中的列)
    @Data
    static class FlightData {
        @ExcelProperty({"引导车开始", "开始_分析误差"})
        private String startAnalysisError;

        @ExcelProperty({"引导车开始", "开始_终端误差(s)"})
        private String startTerminalError;

        @ExcelProperty("机号")
        private String flightNumber;

        @ExcelProperty("机位")
        private String gate;

        @ExcelProperty({"引导车结束", "结束_分析误差"})
        private String endAnalysisError;

        @ExcelProperty({"引导车结束", "结束_终端误差(s)"})
        private String endTerminalError;


        public FlightData(String startAnalysisError, String startTerminalError, String flightNumber,
                          String gate, String endAnalysisError, String endTerminalError) {
            this.startAnalysisError=startAnalysisError;
            this.startTerminalError=startTerminalError;
            this.flightNumber=flightNumber;
            this.gate=gate;
            this.endAnalysisError=endAnalysisError;
            this.endTerminalError=endTerminalError;
        }


    }
}
相关推荐
Grey Zeng9 小时前
Java SE 25新增特性
java·jdk·jdk新特性·jdk25
雨白10 小时前
Java 线程通信基础:interrupt、wait 和 notifyAll 详解
android·java
架构师沉默14 小时前
设计多租户 SaaS 系统,如何做到数据隔离 & 资源配额?
java·后端·架构
Java中文社群15 小时前
重要:Java25正式发布(长期支持版)!
java·后端·面试
每天进步一点_JL17 小时前
JVM 类加载:双亲委派机制
java·后端
用户2986985301417 小时前
Java HTML 转 Word 完整指南
java·后端
渣哥17 小时前
原来公平锁和非公平锁差别这么大
java
渣哥17 小时前
99% 的人没搞懂:Semaphore 到底是干啥的?
java
J2K18 小时前
JDK都25了,你还没用过ZGC?那真得补补课了
java·jvm·后端
kfyty72518 小时前
不依赖第三方,不销毁重建,loveqq 框架如何原生实现动态线程池?
java·架构