EasyExcel如何导出多级且动态的表头

 更新时间:2026年01月14日 09:15:57   作者:小憨憨的牛  
文章介绍了使用EasyExcel库实现Excel多级表头导出的步骤,包括创建表头集合、导出数据集合、设置样式和列宽,以及自动合并表头和测试导出

实现步骤

  1. 创建表头集合List<List<String>> headList,为每个单元格设置完整的表头路径(一级,二级....)
  2. 创建导出数据集合List<List<String>> dataList,每行列数要与最末及表头的列数一致
  3. 定义表头或单元格的样式,列宽等
  4. 导出时应用样式

分析

EasyExcel 会根据你的Head结构自动判断是否需要合并表头。如果你的表头是多层级的(比如有分组表头),EasyExcel 会自动合并相同的一级表头。

动态表头可以根据实际的业务需求组装成List<List<String>>格式,导出数据也是。

一 .业务代码

@Test
    void excelTest(){
        // 1. 动态构建表头(一级和二级)
        List<List<String>> headList = new ArrayList<>();

        // 为每个单元格设置完整的表头路径
        headList.add(new ArrayList<>(Arrays.asList("id")));
        headList.add(new ArrayList<>(Arrays.asList("学院", "学院名称")));
        headList.add(new ArrayList<>(Arrays.asList("学院", "学院代码")));
        headList.add(new ArrayList<>(Arrays.asList("学生信息", "姓名")));
        headList.add(new ArrayList<>(Arrays.asList("学生信息", "学号")));
        headList.add(new ArrayList<>(Arrays.asList("成绩", "数学")));
        headList.add(new ArrayList<>(Arrays.asList("成绩", "语文")));

        // 2. 准备内容数据(每行列数与二级表头一致)
        List<List<Object>> dataList = new ArrayList<>();
        dataList.add(new ArrayList<>(Arrays.asList("123","计算机学院", "CS001", "张三", "S1001", 90, 85)));
        dataList.add(new ArrayList<>(Arrays.asList("324","计算机学院", "CS001", "李四", "S1002", 88, 92)));
        dataList.add(new ArrayList<>(Arrays.asList("966","文学院", "LA002", "王五", "S2001", 78, 95)));

        // 3. 自定义表头字体格式样式
        WriteCellStyle headWriteCellStyle = ExcelHeadStyle.writExcelHeadStyle();
        
        // 4. 应用样式策略
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, new WriteCellStyle());

        // 5. 导出 Excel 并应用样式
        String fileName = "dynamic_header_with_style.xlsx";
        EasyExcel.write(fileName)
                .head(headList)
                .registerWriteHandler(horizontalCellStyleStrategy)  // 注册样式处理器
                .registerWriteHandler(new ExcelCellWriteHandler()) // 注册自适应列宽
                .sheet("学生成绩表")
                .doWrite(dataList);
    }

二. 表头样式实现

/**
 * @Description Excel表头样式
 * @Author dhp
 * @create 2025-06-09 15:13
 */
public class ExcelHeadStyle {

    /**
     * 设置表头字体格式样式
     * @return
     */
    public static WriteCellStyle writExcelHeadStyle(){
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 设置表头居中对齐
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        // 创建字体样式
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontName("宋体");  // 设置字体
        headWriteFont.setFontHeightInPoints((short) 12);  // 设置字号
        headWriteFont.setBold(true);  // 加粗
        headWriteCellStyle.setWriteFont(headWriteFont);

        // 设置背景颜色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        return headWriteCellStyle;
    }

}

三.表头和单元格列宽调整策略

自动适应合适的列宽

**
 * 单元格列宽调整策略
 * @Description
 * @Author dhp
 * @create 2025-06-09 14:55
 */
public class ExcelCellWriteHandler extends AbstractColumnWidthStyleStrategy {
    private static final int MAX_COLUMN_WIDTH = 255;
    private static final int MIN_COLUMN_WIDTH = 8;

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        int columnIndex = cell.getColumnIndex();
        Sheet sheet = writeSheetHolder.getSheet();

        // 如果是表头,直接设置宽度
        if (isHead) {
            String text = cell.getStringCellValue();
            int width = text.length() * 2 + 2;
            width = Math.min(width, MAX_COLUMN_WIDTH);
            width = Math.max(width, MIN_COLUMN_WIDTH);
            sheet.setColumnWidth(columnIndex, width * 256);
        }
        // 如果是内容单元格,计算并比较是否需要更新宽度
        else {
            // 获取当前列宽
            int currentWidth = sheet.getColumnWidth(columnIndex) / 256;

            // 计算内容所需宽度
            int contentWidth = calculateCellWidth(cell);
            contentWidth = Math.min(contentWidth, MAX_COLUMN_WIDTH);
            contentWidth = Math.max(contentWidth, MIN_COLUMN_WIDTH);

            // 如果内容比当前列宽更宽,则更新列宽
            if (contentWidth > currentWidth) {
                sheet.setColumnWidth(columnIndex, contentWidth * 256);
            }
        }
    }

    /**
     * 根据单元格内容计算合适的宽度
     * @param cell
     * @return
     */
    private int calculateCellWidth(Cell cell) {
        String text;
        switch (cell.getCellType()) {
            case STRING:
                text = cell.getStringCellValue();
                break;
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    text = cell.getDateCellValue().toString();
                } else {
                    text = String.valueOf(cell.getNumericCellValue());
                }
                break;
            case BOOLEAN:
                text = String.valueOf(cell.getBooleanCellValue());
                break;
            case FORMULA:
                text = cell.getCellFormula();
                break;
            default:
                text = "";
        }

        // 简单计算:中文字符算2个宽度,其他算1个
        int width = 0;
        for (char c : text.toCharArray()) {
            //0x4E00 到 0x9FA5 是 Unicode 中 CJK 统一表意文字的范围(即中文字符)
            width += (c >= 0x4E00 && c <= 0x9FA5) ? 2 : 1;
        }

        return width + 2; // 加2作为边距
    }
}

四.测试导出

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring Boot整合邮件发送与注意事项

    Spring Boot整合邮件发送与注意事项

    这篇文章主要给大家介绍了关于Spring Boot整合邮件发送与注意事项的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-07-07
  • java数据类型和运算符的深入讲解

    java数据类型和运算符的深入讲解

    这篇文章主要给大家介绍了关于java数据类型和运算符的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 教你使用springSecurity+jwt实现互踢功能

    教你使用springSecurity+jwt实现互踢功能

    JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。接下来通过本文给大家介绍springSecurity+jwt实现互踢功能,需要的朋友可以参考下
    2021-11-11
  • Java System类用法实战案例

    Java System类用法实战案例

    这篇文章主要介绍了Java System类用法,结合具体实例形式分析了java使用System类获取系统环境变量信息相关操作技巧,需要的朋友可以参考下
    2019-07-07
  • Java项目防止SQL注入的几种方式

    Java项目防止SQL注入的几种方式

    SQL注入是一种常见的攻击方式,黑客试图通过操纵应用程序的输入来执行恶意SQL查询,从而绕过认证和授权,窃取、篡改或破坏数据库中的数据,本文主要介绍了Java项目防止SQL注入的几种方式,感兴趣的可以了解一下
    2023-12-12
  • java 二叉查找树实例代码

    java 二叉查找树实例代码

    这篇文章主要介绍了java 二叉查找树实例代码的相关资料,需要的朋友可以参考下
    2017-03-03
  • 使用Java注解模拟spring ioc容器过程解析

    使用Java注解模拟spring ioc容器过程解析

    这篇文章主要介绍了使用Java注解模拟spring ioc容器过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 将Java程序的输出结果写入文件方法实例

    将Java程序的输出结果写入文件方法实例

    这篇文章主要给大家介绍了关于将Java程序的输出结果写入文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • MyBatis 在 Spring Boot 中的实践记录

    MyBatis 在 Spring Boot 中的实践记录

    MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数映射,配置数据库连接与驼峰转换,接下来通过本文给大家介绍破茧JDBC:MyBatis在Spring Boot中的轻量实践指南,感兴趣的哦朋友一起看看吧
    2025-08-08
  • 微信跳一跳辅助Java代码实现

    微信跳一跳辅助Java代码实现

    这篇文章主要为大家详细介绍了微信跳一跳辅助的Java代码实现资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01

最新评论