news 2026/6/24 9:06:27

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 性能差
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/24 9:02:10

从零开始掌握DeepLabCut:动物姿态估计的终极实践指南

从零开始掌握DeepLabCut&#xff1a;动物姿态估计的终极实践指南 【免费下载链接】DeepLabCut Official implementation of DeepLabCut: Markerless pose estimation of user-defined features with deep learning for all animals incl. humans 项目地址: https://gitcode.c…

作者头像 李华
网站建设 2026/6/24 8:58:39

一 智能体经典范式构建

智能体经典范式构建 — 学习笔记来源&#xff1a;《Hello Agents》第四章 | 核心主题&#xff1a;从零实现 ReAct、Plan-and-Solve、Reflection 三大智能体范式一、章节定位与学习目标本章承上启下&#xff1a;上一章讲解了 LLM 的 Transformer 架构与交互方法&#xff0c;本章…

作者头像 李华
网站建设 2026/6/24 8:57:11

包管理器用法速查

前言 不同的操作系统、编程语言几乎都有自己的包管理器&#xff0c;而每种包管理器的命令用法虽都大同小异&#xff0c;但还是有些区别的。为了避免在面临各种命令时出现一个头两个大的情况&#xff0c;特为此专门整理一份简要手册&#xff0c;以便随用随查。 1、Linux 类 Li…

作者头像 李华
网站建设 2026/6/24 8:49:34

iOS 代码混淆工具对比 从源码级混淆到 IPA 直接加固

我注意到一个现象&#xff1a;团队里好几个同事在提测 IPA 之前都会问一句"代码混淆做了没"&#xff0c;但真问到具体用的什么方案、做到什么程度&#xff0c;又都说不太清楚。我之前也一样&#xff0c;直到有次把自己打出来的 IPA 拖进 Hopper 看了一眼——类名、方…

作者头像 李华
网站建设 2026/6/24 8:42:47

AVR单片机JTAG与边界扫描技术:从原理到硬件调试实战

1. 项目概述&#xff1a;从“黑盒子”到“透明调试”在嵌入式开发的早期&#xff0c;调试一个单片机程序&#xff0c;尤其是当它焊死在电路板上、程序跑飞或者IO口状态异常时&#xff0c;那种感觉就像面对一个“黑盒子”。你只能通过有限的串口打印信息&#xff0c;或者观察几个…

作者头像 李华
网站建设 2026/6/24 8:39:09

AVR XMEGA A3U嵌入式开发实战:从GPIO、AES加密到ADC高精度采集

1. 项目概述&#xff1a;为什么是AVR XMEGA A3U&#xff1f;在嵌入式开发的广阔世界里&#xff0c;当你需要一款性能强劲、外设丰富且兼顾安全性的8位微控制器时&#xff0c;AVR XMEGA系列&#xff0c;特别是A3U型号&#xff0c;绝对是一个绕不开的经典选择。它不像某些32位MCU…

作者头像 李华