java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > EasyExcel导出多级且动态的表头

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

作者:小憨憨的牛

文章介绍了使用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作为边距
    }
}

四.测试导出

总结

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

您可能感兴趣的文章:
阅读全文