EasyPoi表头字体及样式自定义

最近在研究EasyPoi的excel导出功能,发现其不支持表头各个单元格样式自定义,所以就对其导出功能进行了重写。

一、EasyPoi源码分析

在进行重写之前,首先要了解EasyPoi源码中,对表头处理的代码具体是如何实现的。查看源码可以看出,导出功能主要是通过ExcelExportService类来实现,而在其中可以看到向excel中填入表头及数据的方法为insertDataToSheet,具体代码如下:

java 复制代码
protected void insertDataToSheet(Workbook workbook, ExportParams entity,
                                     List<ExcelExportEntity> entityList, Collection<?> dataSet,
                                     Sheet sheet) {
    try {
        dataHandler = entity.getDataHandler();
        if (dataHandler != null && dataHandler.getNeedHandlerFields() != null) {
            needHandlerList = Arrays.asList(dataHandler.getNeedHandlerFields());
        }
        dictHandler = entity.getDictHandler();
        commentHandler = entity.getCommentHandler();
        // 创建表格样式
        setExcelExportStyler((IExcelExportStyler) entity.getStyle()
                .getConstructor(Workbook.class).newInstance(workbook));
        Drawing                 patriarch   = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);
        List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
        if (entity.isAddIndex()) {
            excelParams.add(indexExcelEntity(entity));
        }
        excelParams.addAll(entityList);
        sortAllParams(excelParams);
        int index = entity.isCreateHeadRows()
                ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;
        int titleHeight = index;
        setCellWith(excelParams, sheet);
        setColumnHidden(excelParams, sheet);
        short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams);
        setCurrentIndex(1);
        createAddressList(sheet, index, excelParams, 0);
        Iterator<?>  its      = dataSet.iterator();
        List<Object> tempList = new ArrayList<Object>();
        while (its.hasNext()) {
            Object t = its.next();
            index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];
            tempList.add(t);
            if (index >= MAX_NUM) {
                break;
            }
        }
        if (entity.getFreezeCol() != 0) {
            sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
        }

        mergeCells(sheet, excelParams, titleHeight);

        its = dataSet.iterator();
        for (int i = 0, le = tempList.size(); i < le; i++) {
            its.next();
            its.remove();
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("List data more than max ,data size is {}",
                    dataSet.size());
        }
        // 发现还有剩余list 继续循环创建Sheet
        if (dataSet.size() > 0) {
            createSheetForMap(workbook, entity, entityList, dataSet);
        } else {
            // 创建合计信息
            addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
        }

    } catch (Exception e) {
        LOGGER.error(e.getMessage(), e);
        throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
    }
}

而在此方法中进一步分析,可看出表头数据及样式操作的方法为createHeaderAndTitle,跳转后可确定操作表头方法为createHeaderRow,具体代码如下:

java 复制代码
private int createHeaderRow(ExportParams title, Sheet sheet, Workbook workbook, int index,
                                List<ExcelExportEntity> excelParams, int cellIndex) {
    Row row  = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index);
    int rows = getRowNums(excelParams, true);
    row.setHeight(title.getHeaderHeight());
    Row listRow = null;
    if (rows >= 2) {
        listRow = sheet.getRow(index + 1);
        if (listRow == null) {
            listRow = sheet.createRow(index + 1);
            listRow.setHeight(title.getHeaderHeight());

        }
    }
    int       groupCellLength = 0;
    CellStyle titleStyle      = getExcelExportStyler().getTitleStyle(title.getColor());
    for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
        ExcelExportEntity entity = excelParams.get(i);
        // 加入换了groupName或者结束就,就把之前的那个换行
        if (StringUtils.isBlank(entity.getGroupName()) || i == 0 || !entity.getGroupName().equals(excelParams.get(i - 1).getGroupName())) {
            if (groupCellLength > 1) {
                sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1));
            }
            groupCellLength = 0;
        }
        if (StringUtils.isNotBlank(entity.getGroupName())) {
            createStringCell(row, cellIndex, entity.getGroupName(), titleStyle, entity);
            createStringCell(listRow, cellIndex, entity.getName(), titleStyle, entity);
            groupCellLength++;
        } else if (StringUtils.isNotBlank(entity.getName())) {
            createStringCell(row, cellIndex, entity.getName(), titleStyle, entity);
        }
        if (entity.getList() != null) {
            // 保持原来的
            int tempCellIndex = cellIndex;
            cellIndex = createHeaderRow(title, sheet, workbook, rows == 1 ? index : index + 1, entity.getList(), cellIndex);
            List<ExcelExportEntity> sTitel = entity.getList();
            if (StringUtils.isNotBlank(entity.getName()) && sTitel.size() > 1) {
                PoiMergeCellUtil.addMergedRegion(sheet, index, index, tempCellIndex, tempCellIndex + getFieldLength(sTitel));
            }
            /*for (int j = 0, size = sTitel.size(); j < size; j++) {

                createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(),
                        titleStyle, entity);
                cellIndex++;
            }*/
            cellIndex--;
        } else if (rows > 1 && StringUtils.isBlank(entity.getGroupName())) {
            createStringCell(listRow, cellIndex, "", titleStyle, entity);
            PoiMergeCellUtil.addMergedRegion(sheet, index, index + rows - 1, cellIndex, cellIndex);
        }
        cellIndex++;
    }
    if (groupCellLength > 1) {
        PoiMergeCellUtil.addMergedRegion(sheet, index, index, cellIndex - groupCellLength, cellIndex - 1);
    }
    return cellIndex;

}

分析源码可看出,表头的样式主要是根据excelParams参数中存储的样式来设定,而此参数对应的类型(ExcelExportEntity)中,样式字段只有宽度和高度,而这就是EasyPoi不支持自定义表头样式的原因。

通过上述分析,我们可知,若要支持自定义表头样式,我们首先考虑在createHeaderRow方法的中增加表头样式的参数(List<ExcelExportExtendEntity> extendList),而此方法为private方法,所以只能在继承ExcelExportService类的自定义类中重载此方法。继而,需要依次重载createHeaderAndTitle、insertDataToSheet、createSheetForMap。而之所以要依次重载这些方法,主要原因在于,表头自定义样式的获取最好和本身excelParams参数内容的获取放在一起,防止重复调用。而excelParams参数内容是通过getAllExcelField方法来获取,源码如下:

java 复制代码
public void getAllExcelField(String[] exclusions, String targetId, Field[] fields,
                                 List<ExcelExportEntity> excelParams, Class<?> pojoClass,
                                 List<Method> getMethods, ExcelEntity excelGroup) throws Exception {
    List<String>      exclusionsList = exclusions != null ? Arrays.asList(exclusions) : null;
    ExcelExportEntity excelEntity;
    // 遍历整个filed
    for (int i = 0; i < fields.length; i++) {
        Field field = fields[i];
        // 先判断是不是collection,在判断是不是java自带对象,之后就是我们自己的对象了
        if (PoiPublicUtil.isNotUserExcelUserThis(exclusionsList, field, targetId)) {
            continue;
        }
        // 首先判断Excel 可能一下特殊数据用户回自定义处理
        if (field.getAnnotation(Excel.class) != null) {
            Excel  excel = field.getAnnotation(Excel.class);
            String name  = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null);
            if (StringUtils.isNotBlank(name)) {
                excelParams.add(createExcelExportEntity(field, targetId, pojoClass, getMethods, excelGroup));
            }
        } else if (PoiPublicUtil.isCollection(field.getType())) {
            ExcelCollection         excel = field.getAnnotation(ExcelCollection.class);
            ParameterizedType       pt    = (ParameterizedType) field.getGenericType();
            Class<?>                clz   = (Class<?>) pt.getActualTypeArguments()[0];
            List<ExcelExportEntity> list  = new ArrayList<ExcelExportEntity>();
            getAllExcelField(exclusions,
                    StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
                    PoiPublicUtil.getClassFields(clz), list, clz, null, null);
            excelEntity = new ExcelExportEntity();
            excelEntity.setName(PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null));
            if (i18nHandler != null) {
                excelEntity.setName(i18nHandler.getLocaleName(excelEntity.getName()));
            }
            excelEntity.setOrderNum(Integer
                    .valueOf(PoiPublicUtil.getValueByTargetId(excel.orderNum(), targetId, "0")));
            excelEntity
                    .setMethod(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
            excelEntity.setList(list);
            excelParams.add(excelEntity);
        } else {
            List<Method> newMethods = new ArrayList<Method>();
            if (getMethods != null) {
                newMethods.addAll(getMethods);
            }
            newMethods.add(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
            ExcelEntity excel = field.getAnnotation(ExcelEntity.class);
            if (excel.show() && StringUtils.isEmpty(excel.name())) {
                throw new ExcelExportException("if use ExcelEntity ,name mus has value ,data: " + ReflectionToStringBuilder.toString(excel), ExcelExportEnum.PARAMETER_ERROR);
            }
            getAllExcelField(exclusions,
                    StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
                    PoiPublicUtil.getClassFields(field.getType()), excelParams, field.getType(),
                    newMethods, excel.show() ? excel : null);
        }
    }
}

所以,也需要对 getAllExcelField方法进行重载。

二、自定义导出类CustomizeExportService代码

经过上述源码分析后,我们就可以通过继承ExcelExportService类并对其部分方法进行重写和重载,从而完成自定义样式开发。相关代码如下

1、CustomizeExportService类
java 复制代码
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelEntity;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import cn.afterturn.easypoi.exception.excel.ExcelExportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum;
import cn.afterturn.easypoi.util.PoiExcelGraphDataUtil;
import cn.afterturn.easypoi.util.PoiMergeCellUtil;
import cn.afterturn.easypoi.util.PoiPublicUtil;
import cn.afterturn.easypoi.util.PoiReflectorUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.dromara.common.excel.annotation.HeadFontStyle;
import org.dromara.common.excel.annotation.HeadStyle;
import org.dromara.common.excel.domain.ExcelExportExtendEntity;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class CustomizeExportService extends ExcelExportService {
    /**
     * 最大行数,超过自动多Sheet
     */
    private static int MAX_NUM = 60000;


    /**
     * 获取需要导出的全部字段
     *
     * @param targetId 目标ID
     */
    public void getAllExcelField(ExportParams entity, String targetId, Field[] fields,
                                 List<ExcelExportEntity> excelParams, List<ExcelExportExtendEntity> extendParamsList, Class<?> pojoClass,
                                 List<Method> getMethods, ExcelEntity excelGroup, Workbook workbook) throws Exception {
        List<String> exclusionsList = entity.getExclusions() != null ? Arrays.asList(entity.getExclusions()) : null;
        // 遍历整个filed
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            // 先判断是不是collection,在判断是不是java自带对象,之后就是我们自己的对象了
            if (PoiPublicUtil.isNotUserExcelUserThis(exclusionsList, field, targetId)) {
                continue;
            }
            // 首先判断Excel 可能一下特殊数据用户回自定义处理
            if (field.getAnnotation(Excel.class) != null) {
                Excel excel = field.getAnnotation(Excel.class);
                String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null);
                if (StringUtils.isNotBlank(name)) {
                    excelParams.add(createExcelExportEntity(field, targetId, pojoClass, getMethods, excelGroup));
                    extendParamsList.add(createExcelExportExtendEntity(entity, field, pojoClass, workbook));
                }
            } else if (PoiPublicUtil.isCollection(field.getType())) {
                ExcelCollection excel = field.getAnnotation(ExcelCollection.class);
                ParameterizedType pt = (ParameterizedType) field.getGenericType();
                Class<?> clz = (Class<?>) pt.getActualTypeArguments()[0];
                List<ExcelExportEntity> list = new ArrayList<ExcelExportEntity>();
                List<ExcelExportExtendEntity> extendList = new ArrayList<>();
                getAllExcelField(entity,
                        StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
                        PoiPublicUtil.getClassFields(clz), list, extendList, clz, null, null, workbook);

                //ExcelExportEntity
                ExcelExportEntity excelEntity = new ExcelExportEntity();
                excelEntity.setName(PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null));
                if (i18nHandler != null) {
                    excelEntity.setName(i18nHandler.getLocaleName(excelEntity.getName()));
                }
                excelEntity.setOrderNum(Integer
                        .valueOf(PoiPublicUtil.getValueByTargetId(excel.orderNum(), targetId, "0")));
                excelEntity
                        .setMethod(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
                excelEntity.setList(list);
                excelParams.add(excelEntity);

                //ExcelExportExtendEntity
                ExcelExportExtendEntity extendEntity = createExcelExportExtendEntity(entity, field, pojoClass, workbook);
                extendEntity.setExtendList(extendList);
                extendParamsList.add(extendEntity);
            } else {
                List<Method> newMethods = new ArrayList<Method>();
                if (getMethods != null) {
                    newMethods.addAll(getMethods);
                }
                newMethods.add(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
                ExcelEntity excel = field.getAnnotation(ExcelEntity.class);
                if (excel.show() && StringUtils.isEmpty(excel.name())) {
                    throw new ExcelExportException("if use ExcelEntity ,name mus has value ,data: " + ReflectionToStringBuilder.toString(excel), ExcelExportEnum.PARAMETER_ERROR);
                }
                getAllExcelField(entity,
                        StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId,
                        PoiPublicUtil.getClassFields(field.getType()), excelParams, extendParamsList, field.getType(),
                        newMethods, excel.show() ? excel : null, workbook);
            }
        }
    }

    protected void insertDataToSheet(Workbook workbook, ExportParams entity, List<ExcelExportEntity> entityList, List<ExcelExportExtendEntity> extendList, Collection<?> dataSet, Sheet sheet) {
        try {
            dataHandler = entity.getDataHandler();
            if (dataHandler != null && dataHandler.getNeedHandlerFields() != null) {
                needHandlerList = Arrays.asList(dataHandler.getNeedHandlerFields());
            }
            dictHandler = entity.getDictHandler();
            commentHandler = entity.getCommentHandler();
            Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);
            List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
            if (entity.isAddIndex()) {
                excelParams.add(indexExcelEntity(entity));
            }
            excelParams.addAll(entityList);
            sortAllParams(excelParams);
            int index = entity.isCreateHeadRows()
                    ? createHeaderAndTitle(entity, sheet, workbook, excelParams, extendList) : 0;
            int titleHeight = index;
            setCellWith(excelParams, sheet);
            setColumnHidden(excelParams, sheet);
            short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams);
            setCurrentIndex(1);
            createAddressList(sheet, index, excelParams, 0);
            Iterator<?> its = dataSet.iterator();
            List<Object> tempList = new ArrayList<Object>();
            while (its.hasNext()) {
                Object t = its.next();
                index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];
                tempList.add(t);
                if (index >= MAX_NUM) {
                    break;
                }
            }
            if (entity.getFreezeCol() != 0) {
                sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
            }

            mergeCells(sheet, excelParams, titleHeight);

            its = dataSet.iterator();
            for (int i = 0, le = tempList.size(); i < le; i++) {
                its.next();
                its.remove();
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("List data more than max ,data size is {}",
                        dataSet.size());
            }
            // 发现还有剩余list 继续循环创建Sheet
            if (dataSet.size() > 0) {
                createSheetForMap(workbook, entity, entityList, dataSet);
            } else {
                // 创建合计信息
                addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
            }

        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
        }
    }

    @Override
    public void createSheet(Workbook workbook, ExportParams entity, Class<?> pojoClass, Collection<?> dataSet) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Excel export start ,class is {}", pojoClass);
            LOGGER.debug("Excel version is {}",
                    entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
        }
        if (workbook == null || entity == null || pojoClass == null || dataSet == null) {
            throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
        }
        try {
            // 创建表格样式
            setExcelExportStyler((IExcelExportStyler) entity.getStyle()
                    .getConstructor(Workbook.class).newInstance(workbook));
            List<ExcelExportEntity> excelParams = new ArrayList<>();
            List<ExcelExportExtendEntity> extendList = new ArrayList<>();
            i18nHandler = entity.getI18nHandler();
            // 得到所有字段
            Field[] fields = PoiPublicUtil.getClassFields(pojoClass);
            ExcelTarget excelTarget = pojoClass.getAnnotation(ExcelTarget.class);
            String targetId = excelTarget == null ? null : excelTarget.value();
            getAllExcelField(entity, targetId, fields, excelParams, extendList, pojoClass,
                    null, null, workbook);
            //获取所有参数后,后面的逻辑判断就一致了
            createSheetForMap(workbook, entity, excelParams, extendList, dataSet);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
        }
    }

    public void createSheetForMap(Workbook workbook, ExportParams entity,
                                  List<ExcelExportEntity> entityList, List<ExcelExportExtendEntity> extendList, Collection<?> dataSet) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Excel version is {}",
                    entity.getType().equals(ExcelType.HSSF) ? "03" : "07");
        }
        if (workbook == null || entity == null || entityList == null || dataSet == null) {
            throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);
        }
        super.type = entity.getType();
        if (type.equals(ExcelType.XSSF)) {
            MAX_NUM = 1000000;
        }
        if (entity.getMaxNum() > 0) {
            MAX_NUM = entity.getMaxNum();
        }
        Sheet sheet = null;
        try {
            sheet = workbook.createSheet(entity.getSheetName());
        } catch (Exception e) {
            // 重复遍历,出现了重名现象,创建非指定的名称Sheet
            sheet = workbook.createSheet();
        }
        if (dataSet.getClass().getClass().getName().contains("Unmodifiable")) {
            List dataTemp = new ArrayList<>();
            dataTemp.addAll(dataSet);
            dataSet = dataTemp;
        }
        insertDataToSheet(workbook, entity, entityList, extendList, dataSet, sheet);
        if (entity.isReadonly()) {
            sheet.protectSheet(UUID.randomUUID().toString());
        }
        sheet.setForceFormulaRecalculation(true);
        if (isAutoSize(entity, entityList)) {
            for (int len = Math.max(sheet.getRow(0).getLastCellNum(), sheet.getRow(1).getLastCellNum()), i = 0; i < len; i++) {
                sheet.autoSizeColumn(i, true);
            }
        }
    }

    private int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook,
                                     List<ExcelExportEntity> excelParams, List<ExcelExportExtendEntity> extendList) {
        int rows = 0, fieldLength = getFieldLength(excelParams);
        if (entity.getTitle() != null) {
            rows += createTitle2Row(entity, sheet, workbook, fieldLength);
        }
        createHeaderRow(entity, sheet, workbook, rows, excelParams, extendList, 0);
        rows += getRowNums(excelParams, true);
        if (entity.isFixedTitle()) {
            sheet.createFreezePane(0, rows, 0, rows);
        }
        return rows;
    }

    /**
     * 创建表头
     *
     * @param title
     * @param sheet
     * @param workbook
     * @param index
     * @param excelParams excel字段标准信息
     * @param extendList  excel字段额外信息
     * @param cellIndex
     * @return
     */
    private int createHeaderRow(ExportParams title, Sheet sheet, Workbook workbook, int index,
                                List<ExcelExportEntity> excelParams, List<ExcelExportExtendEntity> extendList, int cellIndex) {
        Row row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index);
        int rows = getRowNums(excelParams, true);
        row.setHeight(title.getHeaderHeight());
        Row listRow = null;
        if (rows >= 2) {
            listRow = sheet.getRow(index + 1);
            if (listRow == null) {
                listRow = sheet.createRow(index + 1);
                listRow.setHeight(title.getHeaderHeight());

            }
        }
        int groupCellLength = 0;
//        CellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor());
        for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {
            ExcelExportEntity entity = excelParams.get(i);
            ExcelExportExtendEntity extendEntity = extendList.get(i);
            CellStyle headerCellStyle = extendEntity.getHeaderCellStyle();

            // 加入换了groupName或者结束就,就把之前的那个换行
            if (StringUtils.isBlank(entity.getGroupName()) || i == 0 || !entity.getGroupName().equals(excelParams.get(i - 1).getGroupName())) {
                if (groupCellLength > 1) {
                    sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1));
                }
                groupCellLength = 0;
            }
            if (StringUtils.isNotBlank(entity.getGroupName())) {
                createStringCell(row, cellIndex, entity.getGroupName(), headerCellStyle, entity);
                createStringCell(listRow, cellIndex, entity.getName(), headerCellStyle, entity);
                groupCellLength++;
            } else if (StringUtils.isNotBlank(entity.getName())) {
                createStringCell(row, cellIndex, entity.getName(), headerCellStyle, entity);
            }
            if (entity.getList() != null) {
                // 保持原来的
                int tempCellIndex = cellIndex;
                cellIndex = createHeaderRow(title, sheet, workbook, rows == 1 ? index : index + 1, entity.getList(), extendEntity.getExtendList(), cellIndex);
                List<ExcelExportEntity> sTitel = entity.getList();
                if (StringUtils.isNotBlank(entity.getName()) && sTitel.size() > 1) {
                    PoiMergeCellUtil.addMergedRegion(sheet, index, index, tempCellIndex, tempCellIndex + getFieldLength(sTitel));
                }
                /*for (int j = 0, size = sTitel.size(); j < size; j++) {

                    createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(),
                            titleStyle, entity);
                    cellIndex++;
                }*/
                cellIndex--;
            } else if (rows > 1 && StringUtils.isBlank(entity.getGroupName())) {
                createStringCell(listRow, cellIndex, "", headerCellStyle, entity);
                PoiMergeCellUtil.addMergedRegion(sheet, index, index + rows - 1, cellIndex, cellIndex);
            }
            cellIndex++;
        }
        if (groupCellLength > 1) {
            PoiMergeCellUtil.addMergedRegion(sheet, index, index, cellIndex - groupCellLength, cellIndex - 1);
        }
        return cellIndex;

    }

    private int createAddressList(Sheet sheet, int index, List<ExcelExportEntity> excelParams, int cellIndex) {
        for (int i = 0; i < excelParams.size(); i++) {
            if (excelParams.get(i).isAddressList()) {
                ExcelExportEntity entity = excelParams.get(i);
                CellRangeAddressList regions = new CellRangeAddressList(index,
                        this.type.equals(ExcelType.XSSF) ? 1000000 : 65535, cellIndex, cellIndex);
                DataValidation dataValidation = sheet.getDataValidationHelper().createValidation(sheet.getDataValidationHelper().createExplicitListConstraint(getAddressListValues(entity)), regions);
                // 处理Excel兼容性问题
                if (dataValidation instanceof XSSFDataValidation) {
                    dataValidation.setSuppressDropDownArrow(true);
                    dataValidation.setShowErrorBox(true);
                } else {
                    dataValidation.setSuppressDropDownArrow(false);
                }
                sheet.addValidationData(dataValidation);
            }
            if (excelParams.get(i).getList() != null) {
                cellIndex = createAddressList(sheet, index, excelParams.get(i).getList(), cellIndex);
            } else {
                cellIndex++;
            }
        }
        return cellIndex;
    }


    private String[] getAddressListValues(ExcelExportEntity entity) {
        if (StringUtils.isNotEmpty(entity.getDict())) {
            String[] arr = new String[this.dictHandler.getList(entity.getDict()).size()];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = this.dictHandler.getList(entity.getDict()).get(i).get("dictValue").toString();
            }
            return arr;
        } else if (entity.getReplace() != null && entity.getReplace().length > 0) {
            String[] arr = new String[entity.getReplace().length];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = entity.getReplace()[i].split("_")[0];
            }
            return arr;
        }
        throw new ExcelExportException(entity.getName() + "没有可以创建下来的数据,请addressList不要设置为true");
    }

    private ExcelExportExtendEntity createExcelExportExtendEntity(ExportParams exportParams, Field field, Class<?> pojoClass, Workbook workbook) {
        ExcelExportExtendEntity entity = new ExcelExportExtendEntity();
        entity.setFieldName(field.getName());
        CellStyle headCellStyle = getExcelExportStyler().getTitleStyle(exportParams.getColor());
        setHeadStyle(headCellStyle, field, pojoClass, workbook);//设置表头样式
        setHeadFont(headCellStyle, field, pojoClass, workbook);//设置表头字体
        entity.setHeaderCellStyle(headCellStyle);
        return entity;
    }

    /**
     * 设置表头字体
     *
     * @param headCellStyle
     * @param field
     */
    private void setHeadFont(CellStyle headCellStyle, Field field, Class<?> pojoClass, Workbook workbook) {
        HeadFontStyle fontStyle = field.getAnnotation(HeadFontStyle.class);
        if (null == fontStyle)
            fontStyle = pojoClass.getAnnotation(HeadFontStyle.class);
        if (null != fontStyle) {
            Font font = workbook.createFont();
            font.setBold(fontStyle.bold());
            if (fontStyle.charset() != -1)
                font.setCharSet(fontStyle.charset());
            if (fontStyle.color() != -1)
                font.setColor(fontStyle.color());
            if (fontStyle.fontHeightInPoints() != -1)
                font.setFontHeightInPoints(fontStyle.fontHeightInPoints());
            if (StringUtils.isNotBlank(fontStyle.fontName()))
                font.setFontName(fontStyle.fontName());
            font.setItalic(fontStyle.italic());
            font.setStrikeout(fontStyle.strikeout());
            if (fontStyle.typeOffset() != -1)
                font.setTypeOffset(fontStyle.typeOffset());
            if (fontStyle.underline() != -1)
                font.setUnderline(fontStyle.underline());
            headCellStyle.setFont(font);
        }
    }

    /**
     * 设置表头样式
     *
     * @param headCellStyle
     * @param field
     */
    private void setHeadStyle(CellStyle headCellStyle, Field field, Class<?> pojoClass, Workbook workbook) {
        HeadStyle headStyle = field.getAnnotation(HeadStyle.class);
        if (null == headStyle)
            headStyle = pojoClass.getAnnotation(HeadStyle.class);
        if (null != headStyle) {
            headCellStyle.setFillPattern(headStyle.fillPatternType());
            int[] colorBackArr = headStyle.fillBackgroundColorRGB();
            if (colorBackArr.length == 3) {
                XSSFColor color = new XSSFColor();
                color.setRGB(intToByteArray(getIntFromColor(colorBackArr[0], colorBackArr[1], colorBackArr[2])));
                headCellStyle.setFillBackgroundColor(color);
            }
            int[] colorForeArr = headStyle.fillForegroundColorRGB();
            if (colorForeArr.length == 3) {
                XSSFColor color = new XSSFColor();
                color.setRGB(intToByteArray(getIntFromColor(colorForeArr[0], colorForeArr[1], colorForeArr[2])));
                headCellStyle.setFillForegroundColor(color);
            }

            headCellStyle.setBorderLeft(headStyle.borderLeft());
            headCellStyle.setBorderRight(headStyle.borderRight());
            headCellStyle.setBorderTop(headStyle.borderTop());
            headCellStyle.setBorderBottom(headStyle.borderBottom());
            if (headStyle.leftBorderColor() != -1)
                headCellStyle.setLeftBorderColor(headStyle.leftBorderColor());
            if (headStyle.rightBorderColor() != -1)
                headCellStyle.setRightBorderColor(headStyle.rightBorderColor());
            if (headStyle.topBorderColor() != -1)
                headCellStyle.setTopBorderColor(headStyle.topBorderColor());
            if (headStyle.bottomBorderColor() != -1)
                headCellStyle.setBottomBorderColor(headStyle.bottomBorderColor());
            if (headStyle.fillBackgroundColor() != -1) {
                headCellStyle.setFillBackgroundColor(headStyle.fillBackgroundColor());
            }
            if (headStyle.fillForegroundColor() != -1) {
                headCellStyle.setFillForegroundColor(headStyle.fillForegroundColor());
            }
        }
    }

    private ExcelExportEntity createExcelExportEntity(Field field, String targetId,
                                                      Class<?> pojoClass,
                                                      List<Method> getMethods, ExcelEntity excelGroup) throws Exception {
        Excel excel = field.getAnnotation(Excel.class);
        ExcelExportEntity excelEntity = new ExcelExportEntity();
        excelEntity.setType(excel.type());
        getExcelField(targetId, field, excelEntity, excel, pojoClass, excelGroup);
        if (getMethods != null) {
            List<Method> newMethods = new ArrayList<Method>();
            newMethods.addAll(getMethods);
            newMethods.add(excelEntity.getMethod());
            excelEntity.setMethods(newMethods);
        }
        return excelEntity;
    }


    /**
     * 注解到导出对象的转换
     */
    private void getExcelField(String targetId, Field field, ExcelExportEntity excelEntity,
                               Excel excel, Class<?> pojoClass, ExcelEntity excelGroup) throws Exception {
        excelEntity.setName(PoiPublicUtil.getValueByTargetId(excel.name(), targetId, null));
        excelEntity.setKey(field.getName());
        excelEntity.setWidth(excel.width());
        excelEntity.setHeight(excel.height());
        excelEntity.setNeedMerge(excel.needMerge());
        excelEntity.setMergeVertical(excel.mergeVertical());
        excelEntity.setMergeRely(excel.mergeRely());
        excelEntity.setReplace(excel.replace());
        excelEntity.setOrderNum(
                Integer.valueOf(PoiPublicUtil.getValueByTargetId(excel.orderNum(), targetId, "0")));
        excelEntity.setWrap(excel.isWrap());
        excelEntity.setExportImageType(excel.imageType());
        excelEntity.setSuffix(excel.suffix());
        excelEntity.setDatabaseFormat(excel.databaseFormat());
        excelEntity.setFormat(
                StringUtils.isNotEmpty(excel.exportFormat()) ? excel.exportFormat() : excel.format());
        excelEntity.setStatistics(excel.isStatistics());
        excelEntity.setHyperlink(excel.isHyperlink());
        excelEntity.setMethod(PoiReflectorUtil.fromCache(pojoClass).getGetMethod(field.getName()));
        excelEntity.setNumFormat(excel.numFormat());
        excelEntity.setColumnHidden(excel.isColumnHidden());
        excelEntity.setDict(excel.dict());
        excelEntity.setEnumExportField(excel.enumExportField());
        excelEntity.setTimezone(excel.timezone());
        excelEntity.setAddressList(excel.addressList());
        excelEntity.setDesensitizationRule(excel.desensitizationRule());
        if (excelGroup != null) {
            excelEntity.setGroupName(PoiPublicUtil.getValueByTargetId(excelGroup.name(), targetId, null));
        } else {
            excelEntity.setGroupName(excel.groupName());
        }
        if (i18nHandler != null) {
            excelEntity.setName(i18nHandler.getLocaleName(excelEntity.getName()));
            excelEntity.setGroupName(i18nHandler.getLocaleName(excelEntity.getGroupName()));
        }
    }

    private boolean isAutoSize(ExportParams entity, List<ExcelExportEntity> entityList) {
        if (entity.isAutoSize()) {
            return true;
        }
        AtomicBoolean autoSize = new AtomicBoolean(true);
        entityList.forEach(e -> {
            if (e.getWidth() != 10) {
                autoSize.set(false);
                return;
            }
            if (CollectionUtils.isNotEmpty(e.getList()) && !isAutoSize(entity, e.getList())) {
                autoSize.set(false);
                return;
            }
        });
        return autoSize.get();
    }

    /**
     * rgb转int
     */
    private static int getIntFromColor(int Red, int Green, int Blue) {
        Red = (Red << 16) & 0x00FF0000;
        Green = (Green << 8) & 0x0000FF00;
        Blue = Blue & 0x000000FF;
        return 0xFF000000 | Red | Green | Blue;
    }

    /**
     * int转byte[]
     */
    public static byte[] intToByteArray(int i) {
        byte[] result = new byte[4];
        result[0] = (byte) ((i >> 24) & 0xFF);
        result[1] = (byte) ((i >> 16) & 0xFF);
        result[2] = (byte) ((i >> 8) & 0xFF);
        result[3] = (byte) (i & 0xFF);
        return result;
    }

}
2、自定义样式的存储类ExcelExportExtendEntity:
java 复制代码
import lombok.Data;
import org.apache.poi.ss.usermodel.CellStyle;

import java.util.List;
@Data
public class ExcelExportExtendEntity {
    /**
     * 字段名称
     */
    private String fieldName;
    /**
     * 表头样式
     */
    private CellStyle headerCellStyle;
    /**
     * 子表头样式
     */
    private List<ExcelExportExtendEntity> extendList;
}
3、自定义注解HeadFontStyle、HeadStyle
java 复制代码
import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HeadFontStyle {
    /**
     * The name for the font (i.e. Arial)
     */
    String fontName() default "";

    /**
     * Height in the familiar unit of measure - points
     */
    short fontHeightInPoints() default -1;

    /**
     * Whether to use italics or not
     */
    boolean italic() default false;

    /**
     * Whether to use a strikeout horizontal line through the text or not
     */
    boolean strikeout() default false;

    /**
     * The color for the font
     *
     * @see Font#COLOR_NORMAL
     * @see Font#COLOR_RED
     * @see HSSFPalette#getColor(short)
     * @see IndexedColors
     */
    short color() default -1;

    /**
     * Set normal, super or subscript.
     *
     * @see Font#SS_NONE
     * @see Font#SS_SUPER
     * @see Font#SS_SUB
     */
    short typeOffset() default -1;

    /**
     * set type of text underlining to use
     *
     * @see Font#U_NONE
     * @see Font#U_SINGLE
     * @see Font#U_DOUBLE
     * @see Font#U_SINGLE_ACCOUNTING
     * @see Font#U_DOUBLE_ACCOUNTING
     */

    byte underline() default -1;

    /**
     * Set character-set to use.
     *
     * @see FontCharset
     * @see Font#ANSI_CHARSET
     * @see Font#DEFAULT_CHARSET
     * @see Font#SYMBOL_CHARSET
     */
    int charset() default -1;

    /**
     * Bold
     */
    boolean bold() default false;
}
java 复制代码
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HeadStyle {
    /**
     * Set the type of border to use for the left border of the cell
     */
    BorderStyle borderLeft() default BorderStyle.THIN;

    /**
     * Set the type of border to use for the right border of the cell
     */
    BorderStyle borderRight() default BorderStyle.THIN;

    /**
     * Set the type of border to use for the top border of the cell
     */
    BorderStyle borderTop() default BorderStyle.THIN;

    /**
     * Set the type of border to use for the bottom border of the cell
     */
    BorderStyle borderBottom() default BorderStyle.THIN;

    /**
     * Set the color to use for the left border
     *
     * @see IndexedColors
     */
    short leftBorderColor() default 54;

    /**
     * Set the color to use for the right border
     *
     * @see IndexedColors
     */
    short rightBorderColor() default 54;

    /**
     * Set the color to use for the top border
     *
     * @see IndexedColors
     */
    short topBorderColor() default 54;

    /**
     * Set the color to use for the bottom border
     *
     * @see IndexedColors
     */
    short bottomBorderColor() default 54;


    /**
     * Setting to one fills the cell with the foreground color... No idea about other values
     *
     * @see FillPatternType#SOLID_FOREGROUND
     */
    FillPatternType fillPatternType() default FillPatternType.SOLID_FOREGROUND;

    /**
     * Set the background fill color.
     *
     * @see IndexedColors
     */
    short fillBackgroundColor() default -1;

    /**
     * 按RGB顺序
     *
     * @return rgb颜色
     */
    int[] fillBackgroundColorRGB() default {};

    /**
     * Set the foreground fill color <i>Note: Ensure Foreground color is set prior to background color.</i>
     *
     * @see IndexedColors
     */
    short fillForegroundColor() default -1;

    /**
     * 按RGB顺序
     *
     * @return rgb颜色
     */
    int[] fillForegroundColorRGB() default {};
}
4、工具类EasyPoiExcelUtil

此类是调用自定义导出类来完成excel导出功能。

java 复制代码
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.hutool.core.util.IdUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.excel.service.easypoi.CustomizeExportService;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;

public class EasyPoiExcelUtil {
    public static void exportExcel(String title, String sheetName, List<?> list, Class<?> pojoClass, HttpServletResponse response) {
        try {
            resetResponse(sheetName, response);
            ExportParams exportParams = new ExportParams(title, sheetName);
            Workbook sheets = getWorkbook(exportParams.getType(), list.size());
            new CustomizeExportService().createSheet(sheets, exportParams, pojoClass, list);
//            Workbook sheets = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
            sheets.write(response.getOutputStream());
            sheets.close();
        } catch (Exception e) {
            throw new RuntimeException("导出Excel异常", e);
        }
    }

    /**
     * 重置响应体
     */
    private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
        String filename = encodingFilename(sheetName);
        FileUtils.setAttachmentResponseHeader(response, filename);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
    }

    /**
     * 编码文件名
     */
    public static String encodingFilename(String filename) {
        return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
    }

    private static Workbook getWorkbook(ExcelType type, int size) {
        if (ExcelType.HSSF.equals(type)) {
            return new HSSFWorkbook();
        } else {
            return new XSSFWorkbook();
        }
    }
}
三、仍需改进

目前只自定义了表头字体及样式,若要支持自定义内容字体及样式,则只需要在ExcelExportExtendEntity类中增加内容字体的字段,并按照相同逻辑存储并修改insertDataToSheet方法。

相关推荐
DKPT32 分钟前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy32 分钟前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss2 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续2 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0442 小时前
ReAct模式解读
java·ai
轮到我狗叫了3 小时前
牛客.小红的子串牛客.kotori和抽卡牛客.循环汉诺塔牛客.ruby和薯条
java·开发语言·算法
Volunteer Technology4 小时前
三高项目-缓存设计
java·spring·缓存·高并发·高可用·高数据量
栗子~~4 小时前
bat脚本- 将jar 包批量安装到 Maven 本地仓库
java·maven·jar
Mr.Entropy5 小时前
ecplise配置maven插件
java·maven