1. 程式人生 > 其它 >poi匯出excel同一單元格不同內容不同顏色

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);
        }
    }
}