easyExcel实现动态表头的数据导出,合并单元格,列宽策略

easyExcel导出(非注解)

思路:先拿到表头数据,再去封装表数据。
一.动态表头
List<List<String>> headTitles = Lists.newArrayList();//表头数据
   说明:外层list表示的表头接口,内层list表示的是表头每一列的数据,而内层list的每一位元素,代表这该行的数据,位序为0的是第一行数据,位序为1的是第二行数据,依次往下。
下面展示一些 代码片段

		List<List<String>> headTitles = Lists.newArrayList();
	    headTitles.add(Lists.newArrayList(basicTitle,day,day));//第一列的第一二三行
        headTitles.add(Lists.newArrayList(basicTitle,week,week));//第二列的四一二三行
        headTitles.add(Lists.newArrayList(basicTitle,dutyPosition1,dutyPosition1));//第三列的一二三行
        headTitles.add(Lists.newArrayList(basicTitle,dutyPosition2,dutyPosition2));//第四列的一二三行
        headTitles.add(Lists.newArrayList(basicTitle,dutyPosition3,dutyPosition3));//第五列的一二三行
        headTitles.add(Lists.newArrayList(basicTitle,dutyPosition4,dutyPosition4));//第六列的一二三行
        headTitles.add(Lists.newArrayList(basicTitle,dutyPosition5,dutyPosition5));//第七列的一二三行

   注意:这里的表头都是固定的,后面的表头是动态生成的,动态表头的生成方法有很多种,例如可以通过横向填充的方法,不过如果表头比较复杂,还涉及到合并操作,建议使用一下这种方法,不需要合并策略就可以实现复杂表头的导出。

  teamList.forEach(item->{
            headTitles.add(Lists.newArrayList(basicTitle,empty,item));
        });//这里的empty是空串,

   表头会自动合并,依据是相邻列的行数据如果值相同会自动合并单元格
最终效果展现:在这里插入图片描述
二.表头数据
   表头数据的封装是需要返回一个List<List>,不过不同的是,外层list的数据的位序,代表的是行数,内层list的数据的位序代表的列数,封装的时候,位序不能乱,否则导出的时候,会对不上。这里的代码就不贴了,一个一个塞吧,每一行的数据封装进一个list里面,注意位序,位序为0代表第一列,依次往后

	List<List<String>> rowList= new ArrayList<>(fixedTitleSize+DynamicTitle.size())

三.结果封装返回
   使用response输出文件流,浏览器下载导出Excel文件

 		    List<List<String>> headTitles = (List<List<String>>)responseMap.get("headTitles");//表头数据,通过service查出来就好了
            List<List<String>> rowList = (List<List<String>>)responseMap.get("rowList"); //表数据
	 	    String newFilePath = folder + newFileName;
            ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(new FileOutputStream(newFilePath));
            excelWriterBuilder.head(headTitles);
            ExcelWriter excelWriter = excelWriterBuilder.build();
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet0").build();
            excelWriter.write(rowList,writeSheet);
            excelWriter.finish();

   下面的代码片等同,会自动关闭输出流

			String sheetName = URLEncoder.encode("表名", "UTF-8");
            String fileName = sheetName.concat(String.valueOf(System.currentTimeMillis())).concat(".xls");
            List<List<String>> headTitles = (List<List<String>>)returnMap.get("headTitles");
            //表数据
            List<List<String>> rowList = (List<List<String>>)returnMap.get("rowList");
            //response输出文件流
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            //写入表头,表数据
            EasyExcel.write(response.getOutputStream()).excelType(ExcelTypeEnum.XLS).head(headTitles).sheet("Sheet0").doWrite(rowList);
            catch(Excpetion e){
            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json");
            response.getWriter().println("导出失败");
            }

导出的合并策略,样式还没设置,以后再补全,优化代码去了。
参考文档:easyexcel语雀官方文档


以下补上导出合并策略
合并分向上合并以及左右合并
向上合并引用别的代码如下,加上一些注释吧

   public class ExcelFileCellMergeStrategy implements CellWriteHandler {
    private int[] mergeColumnIndex;//要合并的列 从0开始索引
    private int mergeRowIndex;//合并起始行行 从0开始索引

    public ExcelFileCellMergeStrategy(int[] mergeColumnIndex, int mergeRowIndex) {
        this.mergeColumnIndex = mergeColumnIndex;
        this.mergeRowIndex = mergeRowIndex;
    }

    public ExcelFileCellMergeStrategy() {
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();
        if (curColIndex > mergeRowIndex){
            for (int columnIndex : mergeColumnIndex) {
                if (curColIndex == columnIndex){
                    mergeWithPrevRow(writeSheetHolder, cell ,curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    /**
     * 当前单元格向上合并
     * @param writeSheetHolder
     * @param cell
     * @param curRowIndex
     * @param curColIndex
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //获取当前行的当前列的数据和上一行的当前列数据,通过上一行数据是否相同进行合并
        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1 ).getCell(curColIndex);
        Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();

        //比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
        if (curData.equals(preData)){
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergedRegions.size() && !isMerged; i++) {
                CellRangeAddress cellAddresses = mergedRegions.get(i);
                //若上 一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellAddresses.isInRange(curRowIndex - 1 , curColIndex)){
                    sheet.removeMergedRegion(i);
                    cellAddresses.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellAddresses);
                    isMerged = true;
                }
            }
            //若上一个单元格未被合并,则新增合并单元
            if (!isMerged){
                CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellAddresses);
            }
        }
    }
}

如果是左右合并的话:

CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, 0, curColIndex);

原理是一样的,只不过就是同行的前后比较,改下代码就可以了,自己debug走一遍就知道怎么改了。


以下是设置公共列宽的策略:

//列宽策略
    public class commonColWidthHandler extends AbstractColumnWidthStyleStrategy {

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

        public commonColWidthHandler() {
        }

        protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
            boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
            if (needSetWidth) {
                Map<Integer, Integer> maxColumnWidthMap = (Map)CACHE.get(writeSheetHolder.getSheetNo());
                if (maxColumnWidthMap == null) {
                    maxColumnWidthMap = new HashMap(16);
                    CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
                }

                Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
                if (columnWidth >= 0) {
                    if (columnWidth > 30) {
                        columnWidth = 30;
                    }

                    Integer maxColumnWidth = (Integer)((Map)maxColumnWidthMap).get(cell.getColumnIndex());
                    if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                        ((Map)maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
                        writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 190);
                    }

                }
            }
        }

        private Integer dataLength(List<CellData> 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;
                        case BOOLEAN:
                            return cellData.getBooleanValue().toString().getBytes().length;
                        case NUMBER:
                            return cellData.getNumberValue().toString().getBytes().length;
                        default:
                            return -1;
                    }
                }
            }
        }
    }

以后还有遗漏我再补充。

  • 5
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值