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