0
点赞
收藏
分享

微信扫一扫

easyExcel 动态列以及自适应列宽的实现

alonwang 2023-07-13 阅读 74

easyExcel 动态列以及自适应列宽的实现

在使用 EasyExcel 实现动态表头和数据以及自适应列宽时,可以按照以下步骤进行操作:

1. 动态表头和数据: EasyExcel 提供了 @ExcelProperty 注解来指定对象属性与 Excel 列之间的映射关系。我们可以通过定义一个包含所有可能出现的列名作为键和对应值类型为 String 的 Map,在读取或写入数据时将其转换为 Java 对象。

具体代码示例如下:

public class DynamicHeaderData {
    private Map<String, Object> header;
    private List<Map<String, Object>> data;

    // getter 和 setter 方法

}

在读取 Excel 数据时,使用 ReadSheetsetHeadRowNumber(int) 方法设置标题所在行数,并通过 doRead() 方法获取到标题行和数据行。

// 读取 Excel 数据并返回 DynamicHeaderData 对象
DynamicHeaderData dynamicHeaderData = new DynamicHeaderData();
List<Object> headerRow = null;
List<List<Object>> dataRows = new ArrayList<>();

EasyExcel.read(file).sheet(sheetIndex)
        .registerReadListener(new AnalysisEventListener<>() {
            @Override
            public void invoke(Object object, AnalysisContext context) {
                if (context.getCurrentRowNum() == headRowNumber) {
                    headerRow = (List<Object>) object;
                } else if (context.getCurrentRowNum() > headRowNumber) {
                    dataRows.add((List<Object>) object);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) { }
        }).doRead();

dynamicHeaderData.setHeader(convertToMap(headerRow));
dynamicHeaderData.setData(convertToListOfMaps(dataRows));

在写入 Excel 数据时,通过 WriteSheetsetHead(List<? extends BaseRowModel>) 方法设置标题行,并将数据转换为 List<List<T>> 格式。

    /**
     * 动态头,实时生成头写入
     * 思路是这样子的,先创建List<String>头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据
     *
     * 1. 创建excel对应的实体对象 参照{@link DemoData}
     * 2. 然后写入table即可
     */
    @Test
    public void dynamicHeadWrite() {
        String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx";
        EasyExcel.write(fileName)
            // 这里放入动态头
            .head(head()).sheet("模板")
            .registerWriteHandler(new CustomWidthStyleStrategy()) //拦截器将在第二部分说明
            // 当然这里数据也可以用 List<List<String>> 去传入
            .doWrite(data());
    }

    private List<List<String>> head() {
        List<List<String>> list = new ArrayList<List<String>>();
        List<String> head0 = new ArrayList<String>();
        head0.add("字符串" + System.currentTimeMillis());
        
        List<String> head1 = new ArrayList<String>();
        head1.add("数字" + System.currentTimeMillis());
        
        List<String> head2 = new ArrayList<String>();
        head2.add("日期" + System.currentTimeMillis());
        
        list.add(head0);
        list.add(head1);
        list.add(head2);
        return list;
    }

    private List<List<String>> data() {
        List<List<String>> resList = new ArrayList<List<String>>();
        List<String> dataList = new ArrayList<String>();
        dataList.add("数据1" + System.currentTimeMillis());
        
        List<String> dataList2 = new ArrayList<String>();
        dataList2.add("数据2" + System.currentTimeMillis());
        
        List<String> dataList3 = new ArrayList<String>();
        dataList3.add("数据3" + System.currentTimeMillis());
        
        resList.add(head0);
        resList.add(head1);
        resList.add(head2);
        return resList;
    }

2. 自适应列宽: EasyExcel 默认会根据内容自适应设置单元格的宽度,但有时候默认的宽度可能不够准确。您可以通过继承 AbstractColumnWidthStyleStrategy 或者手动设置每一列的宽度来实现自适应。

/**
 * 表头宽度根据数据内容自适应
 */
public class CustomWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();

    /**
     * 设置列宽
     *
     * @param writeSheetHolder
     * @param cellDataList
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap<>();
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }
                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }
            }
        }
    }

    /**
     * 数据长度
     *
     * @param cellDataList
     * @param cell
     * @param isHead
     * @return
     */
    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length + 1;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length * 2;
                    default:
                        return -1;
                }
            }
        }
    }
}

注意,自适应列宽可能会对性能产生一定影响,特别是在处理大量数据时。如果您需要更高的性能,可以考虑通过统计内容长度并手动设置合适的列宽。

完成该拦截器,记得将该拦截器注册到EasyExcel

EasyExcel.write(fileName)
            // 这里放入动态头
            .head(head()).sheet("模板")
    
    		// 这一步非常关键,如果没有注册,相当于该类白写
            .registerWriteHandler(new CustomWidthStyleStrategy()) //拦截器将在第二部分说明
            
    		// 当然这里数据也可以用 List<List<String>> 去传入
            .doWrite(data());

以上就是使用 EasyExcel 实现动态表头和数据以及自适应列宽的简单示例。根据具体需求和情况进行调整即可。

举报

相关推荐

0 条评论