当前位置: 首页 > news >正文

Apache POI Excel 导出样式美化实战指南

Apache POI Excel 导出样式美化实战指南

一、概述

使用 Apache POI 导出 Excel 时,默认样式非常简陋(无边框、无背景色、列宽不自适应)。在实际项目中,导出给用户的 Excel 文件需要具备良好的可读性,包括表头美化、数据对齐、边框、列宽自适应等。

本文以XSSFWorkbook(.xlsx 格式)为例,系统介绍 POI 中样式相关 API 的使用方法。


二、POI 样式体系结构

Workbook(工作簿) ├── Font(字体)— 控制文字样式 ├── CellStyle(单元格样式)— 控制单元格外观 ├── Sheet(工作表) │ ├── Row(行)— 控制行高 │ │ └── Cell(单元格)— 绑定 CellStyle │ └── 列宽设置

关键关系

  • FontCellStyle引用
  • CellStyleCell引用
  • 一个CellStyle可以被多个Cell共用(推荐复用,减少内存开销)

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

三、核心 API 详解

3.1 字体(Font)

Fontfont=workbook.createFont();font.setBold(true);// 加粗font.setItalic(true);// 斜体font.setFontHeightInPoints((short)12);// 字号(磅)font.setFontName("微软雅黑");// 字体名称font.setColor(IndexedColors.WHITE.getIndex());// 字体颜色font.setUnderline(Font.U_SINGLE);// 下划线font.setStrikeout(true);// 删除线

3.2 单元格样式(CellStyle)

背景填充
CellStylestyle=workbook.createCellStyle();// 设置背景色(必须先设置前景色,再设置填充模式)style.setFillForegroundColor(IndexedColors.ROYAL_BLUE.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);

常用颜色

颜色常量效果
IndexedColors.ROYAL_BLUE深蓝色(适合表头)
IndexedColors.LIGHT_BLUE浅蓝色
IndexedColors.GREY_25_PERCENT浅灰色(适合交替行)
IndexedColors.LIGHT_YELLOW浅黄色
IndexedColors.WHITE白色
IndexedColors.RED红色(适合错误标记)
对齐方式
// 水平对齐style.setAlignment(HorizontalAlignment.CENTER);// 居中style.setAlignment(HorizontalAlignment.LEFT);// 左对齐style.setAlignment(HorizontalAlignment.RIGHT);// 右对齐// 垂直对齐style.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直居中style.setVerticalAlignment(VerticalAlignment.TOP);// 顶部对齐style.setVerticalAlignment(VerticalAlignment.BOTTOM);// 底部对齐
边框
// 四边边框(细线)style.setBorderTop(BorderStyle.THIN);style.setBorderBottom(BorderStyle.THIN);style.setBorderLeft(BorderStyle.THIN);style.setBorderRight(BorderStyle.THIN);// 边框颜色(可选,默认黑色)style.setTopBorderColor(IndexedColors.BLACK.getIndex());style.setBottomBorderColor(IndexedColors.BLACK.getIndex());style.setLeftBorderColor(IndexedColors.BLACK.getIndex());style.setRightBorderColor(IndexedColors.BLACK.getIndex());

边框样式

常量效果
BorderStyle.THIN细线(最常用)
BorderStyle.MEDIUM中等粗线
BorderStyle.THICK粗线
BorderStyle.DASHED虚线
BorderStyle.DOTTED点线
BorderStyle.NONE无边框
自动换行
style.setWrapText(true);// 内容超出列宽时自动换行
绑定字体
style.setFont(font);// 将 Font 对象绑定到 CellStyle

3.3 行高

Rowrow=sheet.createRow(0);row.setHeightInPoints(20);// 行高20磅// 或row.setHeight((short)(20*20));// 单位是 1/20 磅

3.4 列宽

// 固定列宽(单位是 1/256 个字符宽度)sheet.setColumnWidth(0,5000);// 第0列宽度约20个字符// 自适应列宽(根据内容自动调整)sheet.autoSizeColumn(0);// 自适应 + 额外余量(推荐)sheet.autoSizeColumn(0);intwidth=sheet.getColumnWidth(0);sheet.setColumnWidth(0,Math.max(width+512,4000));// 至少4000宽度

注意autoSizeColumn对中文支持不够好,可能偏窄,建议加 512~1024 的余量。


四、完整示例

publicStringexportEmployee(List<EmployeeExportDto>dataList){XSSFWorkbookworkbook=newXSSFWorkbook();Sheetsheet=workbook.createSheet("员工信息");// ===== 1. 定义表头样式 =====FontheaderFont=workbook.createFont();headerFont.setBold(true);headerFont.setColor(IndexedColors.WHITE.getIndex());headerFont.setFontHeightInPoints((short)11);headerFont.setFontName("微软雅黑");CellStyleheaderStyle=workbook.createCellStyle();headerStyle.setFont(headerFont);headerStyle.setFillForegroundColor(IndexedColors.ROYAL_BLUE.getIndex());headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);headerStyle.setAlignment(HorizontalAlignment.CENTER);headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);headerStyle.setBorderTop(BorderStyle.THIN);headerStyle.setBorderBottom(BorderStyle.THIN);headerStyle.setBorderLeft(BorderStyle.THIN);headerStyle.setBorderRight(BorderStyle.THIN);// ===== 2. 定义数据行样式 =====FontdataFont=workbook.createFont();dataFont.setFontHeightInPoints((short)10);dataFont.setFontName("微软雅黑");CellStyledataStyle=workbook.createCellStyle();dataStyle.setFont(dataFont);dataStyle.setAlignment(HorizontalAlignment.LEFT);dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);dataStyle.setWrapText(true);dataStyle.setBorderTop(BorderStyle.THIN);dataStyle.setBorderBottom(BorderStyle.THIN);dataStyle.setBorderLeft(BorderStyle.THIN);dataStyle.setBorderRight(BorderStyle.THIN);// ===== 3. 创建表头行 =====String[]headers={"工号","姓名","部门","手机号","状态","操作人","创建时间"};RowheaderRow=sheet.createRow(0);headerRow.setHeightInPoints(22);for(inti=0;i<headers.length;i++){Cellcell=headerRow.createCell(i);cell.setCellValue(headers[i]);cell.setCellStyle(headerStyle);}// ===== 4. 填充数据行 =====SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");for(inti=0;i<dataList.size();i++){EmployeeExportDtodto=dataList.get(i);Rowrow=sheet.createRow(i+1);row.setHeightInPoints(18);createStyledCell(row,0,dto.getStaffNo(),dataStyle);createStyledCell(row,1,dto.getStaffName(),dataStyle);createStyledCell(row,2,dto.getDeptName(),dataStyle);createStyledCell(row,3,dto.getPhone(),dataStyle);createStyledCell(row,4,dto.getStatusName(),dataStyle);createStyledCell(row,5,dto.getOperatorName(),dataStyle);createStyledCell(row,6,dto.getCreateTime()!=null?sdf.format(dto.getCreateTime()):"",dataStyle);}// ===== 5. 设置列宽自适应 =====for(inti=0;i<headers.length;i++){sheet.autoSizeColumn(i);intcurrentWidth=sheet.getColumnWidth(i);sheet.setColumnWidth(i,Math.max(currentWidth+512,4000));}// ===== 6. 写入文件并上传 =====// ...省略文件操作代码returnossUrl;}/** * 创建带样式的单元格. */privatevoidcreateStyledCell(Rowrow,intcolIndex,Stringvalue,CellStylestyle){Cellcell=row.createCell(colIndex);cell.setCellValue(value!=null?value:"");cell.setCellStyle(style);}

五、样式复用原则

5.1 为什么要复用 CellStyle

POI 中每个 Workbook 最多支持约 64000 个 CellStyle。如果每个 Cell 都 new 一个 CellStyle,数据量大时会报错:

java.lang.IllegalStateException: The maximum number of Cell Styles was exceeded.

5.2 正确做法

// 正确:在循环外创建样式,循环内复用CellStyledataStyle=workbook.createCellStyle();// ... 设置样式属性for(inti=0;i<dataList.size();i++){Rowrow=sheet.createRow(i+1);Cellcell=row.createCell(0);cell.setCellStyle(dataStyle);// 复用同一个样式对象}// 错误:在循环内创建样式for(inti=0;i<dataList.size();i++){CellStylestyle=workbook.createCellStyle();// 每行创建新样式,浪费资源// ...}

5.3 需要多种样式时

如果需要交替行背景色等不同样式,预先创建有限个样式对象:

// 创建两种数据行样式(白色背景 + 浅灰背景)CellStyleevenStyle=createDataStyle(workbook,IndexedColors.WHITE);CellStyleoddStyle=createDataStyle(workbook,IndexedColors.GREY_25_PERCENT);for(inti=0;i<dataList.size();i++){CellStylestyle=(i%2==0)?evenStyle:oddStyle;// 使用对应样式}

六、常见样式模板

6.1 标准表头(蓝底白字)

FontheaderFont=workbook.createFont();headerFont.setBold(true);headerFont.setColor(IndexedColors.WHITE.getIndex());headerFont.setFontHeightInPoints((short)11);CellStyleheaderStyle=workbook.createCellStyle();headerStyle.setFont(headerFont);headerStyle.setFillForegroundColor(IndexedColors.ROYAL_BLUE.getIndex());headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);headerStyle.setAlignment(HorizontalAlignment.CENTER);headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);headerStyle.setBorderTop(BorderStyle.THIN);headerStyle.setBorderBottom(BorderStyle.THIN);headerStyle.setBorderLeft(BorderStyle.THIN);headerStyle.setBorderRight(BorderStyle.THIN);

6.2 标准数据行(左对齐+换行+边框)

FontdataFont=workbook.createFont();dataFont.setFontHeightInPoints((short)10);CellStyledataStyle=workbook.createCellStyle();dataStyle.setFont(dataFont);dataStyle.setAlignment(HorizontalAlignment.LEFT);dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);dataStyle.setWrapText(true);dataStyle.setBorderTop(BorderStyle.THIN);dataStyle.setBorderBottom(BorderStyle.THIN);dataStyle.setBorderLeft(BorderStyle.THIN);dataStyle.setBorderRight(BorderStyle.THIN);

6.3 数字列(右对齐)

CellStylenumberStyle=workbook.createCellStyle();numberStyle.cloneStyleFrom(dataStyle);// 基于数据行样式克隆numberStyle.setAlignment(HorizontalAlignment.RIGHT);// 可选:设置数字格式DataFormatformat=workbook.createDataFormat();numberStyle.setDataFormat(format.getFormat("#,##0.00"));

6.4 错误标记行(红色字体)

FonterrorFont=workbook.createFont();errorFont.setColor(IndexedColors.RED.getIndex());errorFont.setFontHeightInPoints((short)10);CellStyleerrorStyle=workbook.createCellStyle();errorStyle.setFont(errorFont);errorStyle.setAlignment(HorizontalAlignment.LEFT);errorStyle.setBorderTop(BorderStyle.THIN);errorStyle.setBorderBottom(BorderStyle.THIN);errorStyle.setBorderLeft(BorderStyle.THIN);errorStyle.setBorderRight(BorderStyle.THIN);

七、列宽自适应的坑

7.1 中文字符宽度不准

autoSizeColumn对中文计算宽度偏窄,因为 POI 默认按英文字符宽度计算。

解决:自适应后加余量

sheet.autoSizeColumn(i);intwidth=sheet.getColumnWidth(i);sheet.setColumnWidth(i,(int)(width*1.2));// 增加20%

7.2 大数据量性能问题

autoSizeColumn需要遍历该列所有行来计算最大宽度,数据量大时很慢。

解决:数据量 > 1万行时改用固定列宽

if(dataList.size()>10000){// 固定列宽int[]columnWidths={4000,5000,6000,5000,4000,5000,6000};for(inti=0;i<columnWidths.length;i++){sheet.setColumnWidth(i,columnWidths[i]);}}else{// 自适应for(inti=0;i<headers.length;i++){sheet.autoSizeColumn(i);sheet.setColumnWidth(i,Math.max(sheet.getColumnWidth(i)+512,4000));}}

八、最佳实践清单

  1. 样式对象在循环外创建:避免超出 64000 个 CellStyle 限制
  2. 表头和数据行使用不同样式:表头加粗+背景色,数据行左对齐+换行
  3. 所有单元格设置边框:提升可读性
  4. 表头行高适当加大:建议 20-22pt
  5. 数据行开启自动换行:防止长文本被截断
  6. 列宽自适应后加余量:中文内容需要额外 512-1024 的宽度
  7. null 值转空字符串:避免 Cell 显示 “null”
  8. 封装 createStyledCell 方法:减少重复代码
  9. 数字列右对齐:符合阅读习惯
  10. 大数据量用固定列宽:autoSizeColumn 性能差
http://www.jsqmd.com/news/1071819/

相关文章:

  • 如何在3分钟内掌握Penpot:开源设计工具的完整入门指南
  • Django-Material 布局魔法:如何用三行代码重构你的后台管理界面
  • Android设备完整性修复的终极方案:PlayIntegrityFix深度解析与实战指南
  • 采用创新的五反射潜望棱镜技术
  • 如何用last30days-skill构建你的AI研究助理:跨平台情报聚合完整指南
  • 科普知识类1. 铁氟龙电线是什么材质2. FEP与PTFE铁氟龙线缆性能对比3. 1332铁氟龙电线耐温等级解析4. AF200高温线缆氟塑料特性说明5. 铁氟龙电线耐高低温原理科普6. 储能线束为何
  • Hermes Agent企业级可观测性架构:构建生产级AI代理监控体系的最佳实践
  • TypeOff:不止语音转写,自带 AI 润色的口述写作神器
  • Hermes Agent数据库连接池终极配置指南:快速提升系统性能的5个关键步骤
  • 5个步骤掌握专业提示词工程:从新手到专家的完整指南
  • Mastercam软件安装步骤(附安装包)Mastercam2026 R2下载安装教程(图文步骤)
  • 实战指南:使用Stagehand构建高效AI浏览器自动化系统
  • MABR-MLE组合工艺在高氨氮工业废水中的应用
  • 一致性 Hash 超通俗讲解
  • AI进阶三境界:从聊天框到专家团队,你处于哪一层?
  • 再见RAG!AI知识库还得是SAG,又快又准~
  • Go语言高效学习路线 + 超详细对比C语言(从语法到底层思维)
  • AMDVLK完全指南:如何在Linux上释放Radeon显卡的Vulkan性能潜力
  • Doris详细介绍与使用之查询语法(三)
  • 预测系统的监控与告警:当你的模型开始“静默失效”
  • 终极指南:三步掌握DeepLabCut无标记姿态追踪技术
  • RT-Thread的内核对象管理,设计比你想的巧妙
  • V9数据库替换授权
  • Get Shit Done:彻底解决AI编程上下文衰退问题的元提示工程系统
  • 微信小程序开店找哪家公司,正规靠谱怎么选?
  • 微信小游戏Unity适配方案:5个核心挑战与实战优化策略
  • 数据库视图
  • 我在大厂做Agent落地踩过的那些坑
  • 半小时学会 Python 爬虫:从零爬取知乎实时热榜榜单
  • 一行命令生成 PPT:OfficeCLI 让文档自动化彻底告别 50 行 Python