1  背景:

       excel 导出时,可能会遇到 导出列表 表头项 不固定,且每项的宽度不统一,例如:性别 可能只需要 10个宽度,但是地址 可能就需要 30个甚至50个宽度。

2   EasyExcel 官方提供的动态列宽解决方案

2.1   实体类中使用注解

以下代码为官方示例代码

@Getter
@Setter
@EqualsAndHashCode
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class WidthAndHeightData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    /**
     * 宽度为50
     */
    @ColumnWidth(50)
    @ExcelProperty("数字标题")
    private Double doubleData;
}

因为 我们是动态表头,没有固定的实体类没法使用,不符合需求,PASS

2.2  官方提供的自动调整列宽的策略LongestMatchColumnWidthStyleStrategy

实体类

@Getter
@Setter
@EqualsAndHashCode
public class LongestMatchColumnWidthData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题很长日期标题很长日期标题很长很长")
    private Date date;
    @ExcelProperty("数字")
    private Double doubleData;
}

具体代码

   /**
     * 自动列宽(不太精确)
     * <p>
     * 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照
     * {@link LongestMatchColumnWidthStyleStrategy}重新实现.
     * <p>
     * poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。
     *
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link LongestMatchColumnWidthData}
     * <p>
     * 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy}
     * <p>
     * 3. 直接写即可
     */
    @Test
    public void longestMatchColumnWidthWrite() {
        String fileName =
            TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, LongestMatchColumnWidthData.class)
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong());
    }

    private List<LongestMatchColumnWidthData> dataLong() {
        List<LongestMatchColumnWidthData> list = new ArrayList<LongestMatchColumnWidthData>();
        for (int i = 0; i < 10; i++) {
            LongestMatchColumnWidthData data = new LongestMatchColumnWidthData();
            data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(1000000000000.0);
            list.add(data);
        }
        return list;
    }

方案二 和 方案一 有一样的问题,动态表头没有具体的实体类,且 此方案 生成的列宽不太精确,故也PASS  ,但是 这个方案为我们提供了一种思路,我们是否可以通过自定义一个策略来实现动态表头的策略。

3 最终方案:自定义列宽策略

通过读取表头的配置,借助easyexcel 提供的 回调方法对 表头列宽进行设置。

3.1  定义 表头信息的实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TableHeaderVO implements Serializable {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = 1L;

    /**
     * 字段对应数据
     */
    private String prop;

    /**
     * 表头名称
     */
    private String label;

    /**
     * 展示顺序
     */
    private Integer index;

    /**
     * 展示宽度
     */
    private Long width;

    /**
     * 是否自适应
     * true为自适应
     * false为非自适应
     * 默认为false
     */
    private Boolean fixed = false;
}

表头信息,包括表头的列中文名 label、列属性 prop 、列宽 width 等属性,我们可以配置在数据库中,查询后使用TableHeaderVO 类进行接收查询结果。

3.2  自定义列宽策略

public class ColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

  private static final int MAX_COLUMN_WIDTH = 255;

  private Map<String, List<TableHeaderVO>> sheetHeaderMap;

  public ColumnWidthStyleStrategy(Map<String, List<TableHeaderVO>> sheetHeaderMap) {
    this.sheetHeaderMap = sheetHeaderMap;
  }

  @Override
  protected void setColumnWidth(
      WriteSheetHolder writeSheetHolder,
      List<WriteCellData<?>> cellDataList,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {
    final int rowIndex = cell.getRowIndex();
// 只在第一行设置列宽即可   
 if (rowIndex != 0) {
      return;
    }
    final List<TableHeaderVO> list = sheetHeaderMap.get(writeSheetHolder.getSheetName());

    Integer columnWidth = dataLength(list, cell);
    if (columnWidth < 0) {
      return;
    }
    if (columnWidth > MAX_COLUMN_WIDTH) {
      columnWidth = MAX_COLUMN_WIDTH;
    }
    Integer maxColumnWidth = 1;
    if (columnWidth > maxColumnWidth) {
      writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
    }
  }

// 根据 cell 中写入的内容,查找该列配置的表头列宽
  private Integer dataLength(List<TableHeaderVO> list, Cell cell) {

    final String cellValue = cell.getStringCellValue();

    final Optional<TableHeaderVO> headerOptional =
        list.stream().filter(k -> cellValue.equals(k.getLabel())).findFirst();
    return headerOptional.map(tableHeaderVO -> tableHeaderVO.getWidth().intValue()).orElse(1);
  }

最终效果如下:

生效,完美解决 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐