poi导出excel同一单元格不同内容不同颜色

poi导出excel同一单元格不同内容不同颜色

​ 今天做导出报告功能,产品要求同一个单元格,不同内容显示不同的颜色。百度找了下,还真发现有这样的实现。

​ 要求效果如下:

​ 其实实现起来也很简单,和我们正常使用excel去编辑文本类似,我们给指定的文本设置一个字体样式就行了。

// 创建一个富文本
XSSFRichTextString xssfRichTextString = new XSSFRichTextString(realText);
// 创建一个字体样式
XSSFFont font = (XSSFFont)workbook.createFont();
// 创建一个默认颜色下标
DefaultIndexedColorMap defaultIndexedColorMap = new DefaultIndexedColorMap();
// 创建一个颜色
XSSFColor xssfColor = new XSSFColor(new java.awt.Color(Integer.parseInt(color.substring(1), 16)),defaultIndexedColorMap);
// 给字体样式设置颜色
font.setColor(xssfColor);
// 给部分字体应用颜色
xssfRichTextString.applyFont(startIndex, endIndex, font);
// 给单元格赋值
cell.setCellValue(xssfRichTextString);

是不是很简单,但是要注意,这里我只是提供了2007版的excel的代码,如果你要兼容2003版,需要自己修改生成颜色color的代码了,其原理都是一样的。

完整工具类代码:

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 生成excel工具类
 */
public class ExcelUtil {
    
     /**
     * 测试
     */
      public static void main(String[] args) throws IOException {
        File file;
        OutputStream outputStream = new FileOutputStream("D:\\Desktop\\1.xlsx");
        Map<String, List<Object[]>> sheetDataMap = new HashMap<>();
        Map<String, Integer> dataBeginIndexMap = new HashMap<>();
        Map<String, List<String[]>> mergeConfigs = new HashMap<>();
        boolean b = true;

        String sheetName = "第一个sheet表";
        sheetDataMap.put(sheetName,Arrays.asList(new Object[]{"序号", "统计结果"}, new Object[]{"1", "###$#27bb15$正常(1)$$###、###$#de0de1$异常(3)$$###"}));
        dataBeginIndexMap.put(sheetName, 2);
        mergeConfigs.put(sheetName, Arrays.asList(new String[]{"0","1","0","0","广东省","l"},new String[]{"0","1","1","1","广州市","c"}));

        ExcelUtil.exportExcelXLSX(outputStream,sheetDataMap,dataBeginIndexMap,mergeConfigs,b);
    }
    
    
    /**
     *  导出excel,可以有多个sheet工作表
     * @param outputStream 输出流,用于接收excel
     * @param sheetDataMap 数据集,key为sheet的名字,value为sheet的数据列表。
     *          单元格内容有这种格式:###$颜色$内容文本$$###,例如:###$#9922ff$这里是单元格的值$$###,则这段文本会被加上指定颜色。
     * @param dataBeginIndexMap sheetDataMap从excel的哪一行开始填充,从0开始,key为sheet的名字,与sheetDataMap的key对应
     * @param mergeConfigs 合并单元格配置,key为sheet的名字,与sheetDataMap的key对应。value为合并单元格配置。
     * 例如[["0","2","0","2","第一个合并单元格"], ["3","5","0","2","第二个合并单元格","l"]]
     * ,表示第一个单元格合并(A1,A2,A3,B1,B2,B3,C1,C2,C3)9个单元格,默认内容居中。第二个单元格合并(D1,D2,D3,E1,E2,E3,F1,F2,F3)9个单元格,内容居左。
     * ["0","2","0","2","第一个合并单元格","内容对齐方式"]对应[起始列, 终止列,起始行, 终止行, 单元格内容, 内容对齐方式(c-居中,l-居左,r-居右)]
     * 内容对齐方式可以不填,只有5个元素
     * @param isFlushAndCloseStream 是否刷新和关闭输出流,如果是false,需要外层自己关闭
     */
    public static void exportExcelXLSX(OutputStream outputStream, Map<String, List<Object[]>> sheetDataMap, Map<String, Integer> dataBeginIndexMap
            , Map<String, List<String[]>> mergeConfigs, boolean isFlushAndCloseStream) throws IOException {
        // 创建工作簿对象
        XSSFWorkbook workbook = new XSSFWorkbook();
        // 遍历创建每个工作表
        Set<Map.Entry<String, List<Object[]>>> entrySet = sheetDataMap.entrySet();
        int sheetIndex = 0;
        for (Map.Entry<String, List<Object[]>> sheetData : entrySet) {
            int dataBeginIndex = dataBeginIndexMap.getOrDefault(sheetData.getKey(), 0);
            // 创建工作表对象
            XSSFSheet sheet = workbook.createSheet();
            short height = 18;
            sheet.setDefaultRowHeightInPoints(height);
            workbook.setSheetName(sheetIndex++, sheetData.getKey());
            // 创建数据行
            XSSFCellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            // 放数据
            for (Object[] datas : sheetData.getValue()) {
                XSSFRow newRow = sheet.createRow(dataBeginIndex++);
                for (int j = 0; j < datas.length; j++) {
                    String cellValue = datas[j] == null ? "" : datas[j].toString();

                    XSSFRichTextString xssfRichTextString = setColor(cellValue, workbook);

                    newRow.createCell(j).setCellValue(xssfRichTextString);
                    newRow.getCell(j).setCellStyle(cellStyle);
                }
            }
            // 设置表格头,合并单元格
            List<String[]> mergeCellList = mergeConfigs.getOrDefault(sheetData.getKey(), new LinkedList<>());
            for (String[] mergeCell : mergeCellList) {
                String cellValue = mergeCell[4] == null ? "" : mergeCell[4].toString();

                XSSFRichTextString xssfRichTextString = setColor(cellValue, workbook);

                mergeCell(sheet, Integer.valueOf(mergeCell[0]), Integer.valueOf(mergeCell[1])
                        , Integer.valueOf(mergeCell[2]), Integer.valueOf(mergeCell[3]), cellValue, xssfRichTextString, mergeCell.length <= 5 ? null : mergeCell[5]);
            }
            //设置自动列宽
            if (!sheetData.getValue().isEmpty()) {
                for (int i = 0, size = sheetData.getValue().size(); i < size; i++) {
                    sheet.autoSizeColumn(i);
                    sheet.autoSizeColumn(i, true);
                    sheet.setColumnWidth(i, sheet.getColumnWidth(i) * 15 / 10);
                }
            }
        }
        // 写Excel
        workbook.write(outputStream);
        // 关闭流
        if (isFlushAndCloseStream) {
            outputStream.flush();
            outputStream.close();
        }
    }

    /**
     * 设置单元格文本颜色,可以实现一个单元格内多种颜色,只针对2007版的xlsx,不兼容2003版的xls
     * @param cellValue 单元格文本,###$颜色$内容文本$$###,例如:###$#9922ff$这里是单元格的值$$###,则这段文本会被加上指定颜色。
     * @param workbook
     * @return
     * @author chc
     * @date 2022-02-18
     */
    private static XSSFRichTextString setColor(String cellValue, XSSFWorkbook workbook){
        // 捕获颜色和文本内容
        Pattern colorTextPattern = Pattern.compile("###\\$(#[0-9a-fA-F]{6})\\$([\\s\\S]+?)\\$\\$###");
        // 捕获颜色
        Pattern colorPattern = Pattern.compile("###\\$(#[0-9a-fA-F]{6})\\$");
        if (!colorTextPattern.matcher(cellValue).find()) {
            return new XSSFRichTextString(cellValue);
        }
        // 真实文本
        String realText = colorPattern.matcher(cellValue).replaceAll("").replaceAll("\\$\\$###", "");
        XSSFRichTextString xssfRichTextString = new XSSFRichTextString(realText);
        // 判断颜色
        Matcher matcher = colorTextPattern.matcher(cellValue);

        int startIndex = 0, endIndex = 0;
        while (matcher.find( )) {
            String color = matcher.group(1);
            String text = matcher.group(2);

            XSSFFont font = (XSSFFont)workbook.createFont();
            DefaultIndexedColorMap defaultIndexedColorMap = new DefaultIndexedColorMap();
            XSSFColor xssfColor = new XSSFColor(new java.awt.Color(Integer.parseInt(color.substring(1), 16)),defaultIndexedColorMap);
            font.setColor(xssfColor);
            // 文本位置
            startIndex = realText.indexOf( text, startIndex);
            endIndex = startIndex + text.length();

            xssfRichTextString.applyFont(startIndex, endIndex, font);
        }
        return xssfRichTextString;
    }

    /**
     * 合并单元格
     * @param sheet
     * @param startCol 起始列,下标从0开始
     * @param endCol 终止列
     * @param startRow 起始行,行下标从0开始
     * @param endRow 终止行
     * @param text 合并后的单元格文本
     * @param richTextString 富文本,如果不为空,单元格就填充richTextString。如果为空,单元格就填充text
     * @param align 文本对齐方式(c-居中,l-居左,r-居右),大小写不区分,默认居中
     * @author chc
     * @date 2020-12-22
     */
    @SuppressWarnings("unused")
    private static void mergeCell(Sheet sheet, int startCol, int endCol, int startRow, int endRow, String text, RichTextString richTextString, String align) {
        boolean isMerged = false;
        if (startRow == endRow && startCol == endCol) {
            // 如果只是一个单元格,没必要合并
            isMerged = false;
        } else {
            // 创建合并单元格
            CellRangeAddress cra = new CellRangeAddress(startRow, endRow, startCol, endCol);
            sheet.addMergedRegion(cra);
            isMerged = true;
        }
        // 设置单元格文本
        Row row = sheet.getRow(startRow) == null ? sheet.createRow(startRow) : sheet.getRow(startRow);
        Cell cell = row.getCell(startCol) == null ? row.createCell(startCol) : row.getCell(startCol);
        if (isMerged) {
            //合并的单元格样式
            CellStyle style = sheet.getWorkbook().createCellStyle();
            //设置居中
            style.setVerticalAlignment(VerticalAlignment.CENTER);
            if (StringUtils.isNotBlank(align)) {
                if ("l".equalsIgnoreCase(align)) {
                    style.setAlignment(HorizontalAlignment.LEFT);
                } else if ("r".equalsIgnoreCase(align)) {
                    style.setAlignment(HorizontalAlignment.RIGHT);
                } else {
                    style.setAlignment(HorizontalAlignment.CENTER);
                }
            } else {
                style.setAlignment(HorizontalAlignment.CENTER);
            }
            cell.setCellStyle(style);
        }
        // 填单元格内容
        if (Objects.nonNull(richTextString)){
            cell.setCellValue(richTextString);
        }else{
            cell.setCellValue(text);
        }
    }
}

posted @ 2022-03-08 19:14  _ME  阅读(3226)  评论(0编辑  收藏  举报