EasyExcel為單個Cell設定樣式

桔子214032發表於2019-01-07

EasyExcel是阿里巴巴對POI封裝的一個庫,號稱解決了POI的OOM問題,並且在使用上也更方便一些

Github:

然而我在使用的時候發現還是有很多坑,其中一個比較頭疼的是對單個單元格樣式的設定。EasyExcel提供了一個BaseRowModel作為每行資料的一個模型,並且其中有一個屬性cellStyleMap代表每列樣式的集合,本來我以為這個只要在自己定義模型的時候,也把CellStyle定義進去就行了,然而,還是我想多了……定義了CellStyle並沒有什麼卵用,這是第一個蛋疼的地方

/**
 * Excel基礎模型
 * @author jipengfei
 */
public class BaseRowModel {

    /**
     * 每列樣式
     */
    private Map<Integer,CellStyle> cellStyleMap = new HashMap<Integer,CellStyle>();

    public void addStyle(Integer row, CellStyle cellStyle){
        cellStyleMap.put(row,cellStyle);
    }

    public CellStyle getStyle(Integer row){
        return cellStyleMap.get(row);
    }

    public Map<Integer, CellStyle> getCellStyleMap() {
        return cellStyleMap;
    }

    public void setCellStyleMap(Map<Integer, CellStyle> cellStyleMap) {
        this.cellStyleMap = cellStyleMap;
    }
}
複製程式碼

後來測試半天,才發現建立CellStyle時必須通過一個Workbook物件來建立,而這個Workbook不能隨便新建一個物件完事兒,得用你當前寫入的Workbook來建立對應的CellStyle樣式才能起作用。然而……事情並沒有那麼簡單,經過我對原始碼的反覆檢視,EasyExcel生成excel表的步驟是用一個ExcelWriter來寫入資料,並沒有提供獲取Workbook的方法,不知道什麼原因讓阿里巴巴不提供這樣一個介面……這是第二個蛋疼的地方

既然沒有提供介面,那就只能用反射來硬剛了,下面就直接上程式碼了

我這裡是在開始寫資料之前就將每張表的CellStyle與每張表關聯起來,再在後面的handler中獲取到這個CellStyle進行設定

package edu.uddp.util;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.event.WriteHandler;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.ExcelBuilderImpl;
import com.sun.corba.se.spi.orbutil.threadpool.Work;
import edu.uddp.enums.CellStyleEnum;
import edu.uddp.model.SignExcelRow;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFSheet;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 生成Excel表
 *
 * @author Juzi
 * @date 2018/12/23 12:37
 * Blog https://juzibiji.top
 */
public class ExcelUtil {

    private static Map<Workbook, Map<String, CellStyle>> cellStyles = new HashMap<>();

    /**
     * 使用java物件模型建立excel,並使用handler
     * 生成Excel格式為xlsx
     *
     * @param path           Excel生成路徑
     * @param headLineMun    表頭佔幾行
     * @param data           傳入的鍵值對資料(key為sheet名,value為sheet資料)
     * @param handler        自定義的EasyExcel Handler,不使用傳入null即可
     * @param columnWidthMap 每列寬度
     * @throws IOException
     */
    public static void createExcelWithModelAndHandler(
            String path, int headLineMun, Map<String,
            List<? extends BaseRowModel>> data, WriteHandler handler,
            Map<Integer, Integer> columnWidthMap, List<CellStyleEnum> cellStyleEnums) throws IOException {

        File file = new File(path);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        OutputStream out = new FileOutputStream(path);
        // ExcelWriter用於匯出Excel
        ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out,
                ExcelTypeEnum.XLSX, true, handler);

        // 構造單元格樣式
        Workbook workbook = getWorkbook(writer);
        cellStyles.put(workbook, createCellStyle(workbook, cellStyleEnums));

        // sheet的序號,從1開始
        int i = 1;
        // 遍歷傳入的sheet名和sheet資料來建立sheet
        for (Map.Entry<String, List<? extends BaseRowModel>> entry : data.entrySet()) {
            Sheet sheet = new Sheet(i, headLineMun, entry.getValue().get(0).getClass(), entry.getKey(), null);
            sheet.setColumnWidthMap(columnWidthMap);
            writer.write(entry.getValue(), sheet);
            i++;
        }
        // 必須要呼叫finish(),否則資料不會寫入檔案
        writer.finish();
        out.close();
    }


    /**
     * **獲取workbook**
     * 因為EasyExcel這個庫設計的原因
     * 只能使用反射獲取workbook
     *
     * @param writer
     * @return
     */
    private static Workbook getWorkbook(ExcelWriter writer) {
        Workbook workbook = null;
        try {
            Class<?> clazz1 = Class.forName("com.alibaba.excel.ExcelWriter");
            Field[] fs = clazz1.getDeclaredFields();
            for (Field field : fs) {
                // 要設定屬性可達,不然會丟擲IllegalAccessException異常
                field.setAccessible(true);
                if ("excelBuilder".equals(field.getName())) {
                    ExcelBuilderImpl excelBuilder = (ExcelBuilderImpl) field.get(writer);
                    Class<?> clazz2 = Class.forName("com.alibaba.excel.write.ExcelBuilderImpl");
                    Field[] fs2 = clazz2.getDeclaredFields();
                    for (Field field2 : fs2) {
                        field2.setAccessible(true);
                        if ("context".equals(field2.getName())) {
                            WriteContext context = (WriteContext) field2.get(excelBuilder);
                            workbook = context.getWorkbook();
                        }
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return workbook;
    }

    public static Map createCellStyle(Workbook workbook, List<CellStyleEnum> cellStyleEnums) {
        Map<String, CellStyle> map = new HashMap<>();
        for (CellStyleEnum cellStyleEnum : cellStyleEnums) {
            if (cellStyleEnum.getNo() == 1) {
                CellStyle cellStyle = workbook.createCellStyle();
                cellStyle.setBorderBottom(BorderStyle.THIN); //下邊框
                cellStyle.setBorderLeft(BorderStyle.THIN);//左邊框
                cellStyle.setBorderTop(BorderStyle.THIN);//上邊框
                cellStyle.setBorderRight(BorderStyle.THIN);//右邊框
                cellStyle.setAlignment(HorizontalAlignment.CENTER);
                map.put(cellStyleEnum.getName(), cellStyle);
            } else if (cellStyleEnum.getNo() == 2) {
                CellStyle cellStyle = workbook.createCellStyle();
                cellStyle.setBorderBottom(BorderStyle.THIN); //下邊框
                cellStyle.setBorderLeft(BorderStyle.THIN);//左邊框
                cellStyle.setBorderTop(BorderStyle.THIN);//上邊框
                cellStyle.setBorderRight(BorderStyle.THIN);//右邊框
                cellStyle.setAlignment(HorizontalAlignment.CENTER);
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
                map.put(cellStyleEnum.getName(), cellStyle);
            }
        }
        return map;
    }

    public static Map<Workbook, Map<String, CellStyle>> getCellStyles() {
        return cellStyles;
    }
}
複製程式碼

EasyExcel提供了一個WriteHandler,我們實現這個介面,就可以在每個單元格寫入之後或者每行寫入之前來進行攔截(這個handler設計的也很蛋疼),並注入我們自己的業務邏輯(設定單元格樣式)。

package edu.uddp.handler;

import com.alibaba.excel.event.WriteHandler;
import edu.uddp.util.ExcelUtil;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFSheet;

import java.util.Map;

/**
 * 第三方庫EasyExcel的Handler
 * 教師端生成簽到歷史表Excel時
 * 將未簽到學生進行特殊標識
 *
 * @author Juzi
 * @since 2018/12/22 22:07
 * Blog https://juzibiji.top
 */
public class SignRecordExcelHandler implements WriteHandler {
    @Override
    public void sheet(int i, Sheet sheet) {

    }

    @Override
    public void row(int i, Row row) {

    }

    @Override
    public void cell(int i, Cell cell) {
        // 獲取當前workbook對應的CellStyle集合
        Map<String, CellStyle> cellStyleMap = ExcelUtil.getCellStyles().get(cell.getSheet().getWorkbook());

        // 從第二行開始設定格式,第一行是表頭
        if (cell.getRowIndex() > 0) {
            if (i == 7 && "未簽到".equals(cell.getStringCellValue())) {
                // 該生未簽到
                for (int j = 0; j < 8; j++) {
                    cell.getRow().getCell(j).setCellStyle(cellStyleMap.get("未簽到"));
                }
            } else if (i == 8 && "已簽到".equals(cell.getRow().getCell(7).getStringCellValue())) {
                // 該生已簽到
                for (int j = 0; j < 9; j++) {
                    cell.getRow().getCell(j).setCellStyle(cellStyleMap.get("已簽到"));
                }
            }else if(i == 8 && "未簽到".equals(cell.getRow().getCell(7).getStringCellValue())){
                cell.setCellStyle(cellStyleMap.get("已簽到"));
            }
        }
    }
}

複製程式碼

上面有一些簡單的邏輯處理,就不一一介紹了。

若文章有任何問題,歡迎留言指出——作者部落格:桔子筆記

相關文章