项目场景
最近项目使用POI按模板
导出Excel, 需要设置一些单元格的字体为红色
. 这里遇到一个容易踩坑的点,所以记录一下,希望能让更多的人少走弯路.
问题描述
我只想修改某个单元格字体为红色,心想这还不简单吗?Cell->CellStyle->Font->color,看起来是那么湿滑,红色是生效了,但。。。没想到却影响了很多类似的单元格。Why?
先上代码:我相信这应该是99%
的人的逻辑:
- 获取要修改的cell.CellStyle和Font
- 设置cell.Font颜色为红色:IndexedColors.RED.getIndex()
- 将修改设置回cell.Font和CellStyle
for (Cell cell : redColorCellList) {
CellStyle cellStyle = cell.getCellStyle();
Font font = workbook.getFontAt(cellStyle.getFontIndexAsInt());
font.setColor(IndexedColors.RED.getIndex());
cellStyle.setFont(font);
cell.setCellStyle(cellStyle);
}
必备基础:设置新创建的单元格样式
我的项目场景是需要修改
单元格字体为红色,但这里有必要先提下新创建
的单元格如何设置样式的。
设置基本样式的示例代码:
新建 excel和sheet
代码语言:javascript复制// 新建 excel
Workbook workbook = new XSSFWorkbook();
// 新建一个 sheet
Sheet sheet = workbook.createSheet();
创建单元格样式:
代码语言:javascript复制CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex()); // 背景色: 浅黄色
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 背景色填充样式:单色填充
cellStyle.setAlignment(HorizontalAlignment.CENTER); // 水平布局:居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直布局:居中
cellStyle.setBorderBottom(BorderStyle.DOUBLE); // 双边框
cellStyle.setBorderLeft(BorderStyle.THIN);// 薄边框
cellStyle.setBorderRight(BorderStyle.DOUBLE); // 双边框
cellStyle.setBorderTop(BorderStyle.THIN);// 薄边框
cellStyle.setBottomBorderColor(IndexedColors.WHITE.getIndex()); // 下边框:白色
cellStyle.setRightBorderColor(IndexedColors.GREEN.getIndex()); // 右边框:绿色
cellStyle.setWrapText(true); // 文本自动换行
设置字体,设置字体颜色
的关键代码:font.setColor(IndexedColors.BLACK.getIndex());,这里IndexedColors里很多颜色可以选择。
Font font = workbook.createFont();
font.setBold(false); // 加粗
font.setFontName("微软雅黑"); // 字体
font.setFontHeightInPoints((short) 14); // 字体高度
font.setColor(IndexedColors.BLACK.getIndex()); // 字体颜色:黑色
cellStyle.setFont(font);
这里为了看效果,我创建一个5行*5列的表格:
代码语言:javascript复制for (int i = 0; i < 5 ; i ) {
// 设置列宽
sheet.setColumnWidth(i, 30 * 160);
// 新增一行 row
Row row = sheet.createRow(i);
row.setHeightInPoints(40);
for (int j = 0; j < 5; j ) {
// 新增一个单元格 cell
Cell cell = row.createCell(j);
cell.setCellValue("Hello_" i j);
cell.setCellStyle(cellStyle);
}
}
保存excel文件
代码语言:javascript复制FileOutputStream outputStream = new FileOutputStream("D:\poi-excel-style-demo.xlsx");
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
workbook.close();
看下效果
解决方案:修改单元格颜色
基于上面我们知道:如果是设置新创建的cell的样式,我们直接新建cellStyle并设置 cell.setCellStyle(cellStyle); 即可,
回到我遇到的问题,因为我们的项目需求是按模板导出,原有的样式不能改,只是将字的颜色修改一下
,那么我们该如何做呢?
还是基于上面的代码,我们只修改第一行 为 红色
字体,并加粗,其它样式不变,先上效果:
这里关键点有两步,也是容易踩坑的点:
- 克隆Style:新建的redCellStyle要从现有的cell拷贝cellStyle:redCellStyle.cloneStyleFrom(cell.getCellStyle());
- 克隆Font:新建的redFont要从现有的cellStyle拷贝font:这个POI没有封装,只能手动拷贝。。。
实现代码如下:
代码语言:javascript复制// 设置第一行为红色,并加粗
CellStyle redCellStyle = null;
for (int m = 0; m < 5; m ) {
Cell cell = sheet.getRow(0).getCell(m);
if (redCellStyle == null) {
redCellStyle = workbook.createCellStyle();
// 重点:从现有样式克隆style,只修改Font,其它style不变
redCellStyle.cloneStyleFrom(cell.getCellStyle());
// 获取原有字体
Font oldFont = workbook.getFontAt(redCellStyle.getFontIndexAsInt());
// 创建新字体
Font redFont = workbook.createFont();
// 重点:保留原字体样式
redFont.setFontName(oldFont.getFontName()); // 保留原字体
redFont.setFontHeightInPoints(oldFont.getFontHeightInPoints()); // 保留原字体高度
redFont.setBold(true); // 加粗
redFont.setColor(IndexedColors.RED.getIndex()); // 字体颜色:红色
// 设置红色字体
redCellStyle.setFont(redFont);
}
// 设置样式
cell.setCellStyle(redCellStyle);
}
那么我也做了相应的封装,让修改Font方法变得更通用,拿走不谢,如果对你有帮助,也请点赞支持,你的鼓励也是我创作的动力~
代码语言:javascript复制public static void setCellFont(List<Cell> cellList, FontParam fontParam) {
if (CollectionUtils.isEmpty(cellList) || fontParam == null) {
return;
}
CellStyle cellStyle = null;
for (Cell cell : cellList) {
if (cellStyle == null) {
Workbook workbook = cell.getSheet().getWorkbook();
cellStyle = workbook.createCellStyle();
// 从现有样式克隆style,只修改Font,其它style不变
cellStyle.cloneStyleFrom(cell.getCellStyle());
// 获取原有字体
Font oldFont = workbook.getFontAt(cellStyle.getFontIndexAsInt());
// 创建新字体
Font newFont = workbook.createFont();
newFont.setFontName(fontParam.getFontName() == null? oldFont.getFontName(): fontParam.getFontName());
newFont.setFontHeightInPoints(fontParam.getFontHeightInPoints() == null? oldFont.getFontHeightInPoints(): fontParam.getFontHeightInPoints());
newFont.setBold(fontParam.getBold() == null? oldFont.getBold(): fontParam.getBold());
newFont.setItalic(fontParam.getItalic() == null? oldFont.getItalic(): fontParam.getItalic());
newFont.setStrikeout(fontParam.getStrikeout() == null? oldFont.getStrikeout(): fontParam.getStrikeout());
newFont.setUnderline(fontParam.getUnderline() == null? oldFont.getUnderline(): fontParam.getUnderline());
newFont.setColor(fontParam.getColor() == null? oldFont.getColor(): fontParam.getColor());
// 设置字体
cellStyle.setFont(newFont);
}
// 设置样式
cell.setCellStyle(cellStyle);
}
}
/**
* 字体参数类,为null代表不设置
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class FontParam {
/**
* 字体名
*/
private String fontName;
/**
* 字体像素高度
*/
private Short fontHeightInPoints;
/**
* 是否加粗
*/
private Boolean bold;
/**
* 是否斜体
*/
private Boolean italic;
/**
* 是否删除线
*/
private Boolean strikeout;
/**
* 下划线类型
* @see #U_NONE
* @see #U_SINGLE
* @see #U_DOUBLE
* @see #U_SINGLE_ACCOUNTING
* @see #U_DOUBLE_ACCOUNTING
*/
private Byte underline;
/**
* 字体颜色
*/
private Short color;
/**
* not underlined
*/
public final static byte U_NONE = 0;
/**
* single (normal) underline
*/
public final static byte U_SINGLE = 1;
/**
* double underlined
*/
public final static byte U_DOUBLE = 2;
/**
* accounting style single underline
*/
public final static byte U_SINGLE_ACCOUNTING = 0x21;
/**
* accounting style double underline
*/
public final static byte U_DOUBLE_ACCOUNTING = 0x22;
}
参考
POI设置单个单元格的样式 POI设置某一单元格的字体颜色