EasyExcel如何导出多级且动态的表头
作者:小憨憨的牛
文章介绍了使用EasyExcel库实现Excel多级表头导出的步骤,包括创建表头集合、导出数据集合、设置样式和列宽,以及自动合并表头和测试导出
实现步骤
- 创建表头集合List<List<String>> headList,为每个单元格设置完整的表头路径(一级,二级....)
- 创建导出数据集合List<List<String>> dataList,每行列数要与最末及表头的列数一致
- 定义表头或单元格的样式,列宽等
- 导出时应用样式
分析
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作为边距
}
}四.测试导出

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