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方法。

相关推荐
Daniel 大东7 分钟前
BugJson因为json格式问题OOM怎么办
java·安全
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸5 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象6 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了6 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·6 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic7 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王7 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康7 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神7 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式