POI设置某一单元格的字体颜色等样式(踩坑记录)

2022-12-02 15:29:10 浏览数 (2)

项目场景

最近项目使用POI按模板导出Excel, 需要设置一些单元格的字体为红色. 这里遇到一个容易踩坑的点,所以记录一下,希望能让更多的人少走弯路.


问题描述

我只想修改某个单元格字体为红色,心想这还不简单吗?Cell->CellStyle->Font->color,看起来是那么湿滑,红色是生效了,但。。。没想到却影响了很多类似的单元格。Why?

先上代码:我相信这应该是99%的人的逻辑:

  1. 获取要修改的cell.CellStyle和Font
  2. 设置cell.Font颜色为红色:IndexedColors.RED.getIndex()
  3. 将修改设置回cell.Font和CellStyle
代码语言:javascript复制
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里很多颜色可以选择。

代码语言:javascript复制
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); 即可, 回到我遇到的问题,因为我们的项目需求是按模板导出,原有的样式不能改,只是将字的颜色修改一下,那么我们该如何做呢?

还是基于上面的代码,我们只修改第一行 为 红色字体,并加粗,其它样式不变,先上效果:

这里关键点有两步,也是容易踩坑的点:

  1. 克隆Style:新建的redCellStyle要从现有的cell拷贝cellStyle:redCellStyle.cloneStyleFrom(cell.getCellStyle());
  2. 克隆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设置某一单元格的字体颜色

0 人点赞