一、概述
虽然EasyExcel
已经提供了一系列注解方式去设置样式。
但是如果没有实体类,或者想要更精确的去设置导出文件的Excel
样式的时候就需要在代码层面去控制样式了。
二、使用已有拦截器自定义样式
主要步骤:
- 创建
Excel
对应的实体对象 - 创建一个
style
策略 并注册 - 写出
Excel
第一步是否需要创建Excel实体对象,得根据实际需求而定,如果导出字段不固定则使用无模型的方式即可
不使用实体类时可以直接传入List
String>>类型的数据来作为表头和数据内容。
2.1 定义一个Excel实体类
@Getter@Setter@EqualsAndHashCodepublic class DemoData { @ExcelProperty("字符串") private String string; @ExcelProperty("日期") private Date date; @ExcelProperty("数字") private Double doubleData; @ExcelIgnore private String ignore;}
2.2 设置表头样式
// 创建一个写出的单元格样式对象WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为红色headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());// 创建写出Excel的字体对象WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short)20);// 设置字体大小为20headWriteFont.setItalic(BooleanEnum.TRUE.getBooleanValue());// 设置字体斜体headWriteCellStyle.setWriteFont(headWriteFont);// 把字体对象设置到单元格样式对象中
2.3 设置内容样式
// 创建一个写出的单元格样式对象WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND// 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);// 设置内容背景色为绿色contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());// 边框设置contentWriteCellStyle.setBorderTop(BorderStyle.THIN); // 设置单元格上边框为细线contentWriteCellStyle.setBorderBottom(BorderStyle.THICK); // 设置单元格下边框为粗线contentWriteCellStyle.setBorderLeft(BorderStyle.MEDIUM); // 设置单元格左边框为中线contentWriteCellStyle.setBorderRight(BorderStyle.MEDIUM_DASHED); // 设置单元格右边框为中虚线// 创建写出Excel的字体对象WriteFont contentWriteFont = new WriteFont();contentWriteFont.setFontHeightInPoints((short)20); //设置字体大小contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); //设置文字居中contentWriteCellStyle.setWriteFont(contentWriteFont); // 把字体对象设置到单元格样式对象中
2.4 使用EasyExcel默认的拦截器策略自定义样式
常见的策略有两种:
-
HorizontalCellStyleStrategy
:每一行的样式都一样 或者隔行一样源码中它主要有两个构造函数:
// 构造函数一:接收一个WriteCellStyle对象和一个List
集合 // 第一个参数是表头部分单元格的样式// 第二个参数是内容部分的单元格样式public HorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle, List<WriteCellStyle> contentWriteCellStyleList) { this.headWriteCellStyle = headWriteCellStyle; this.contentWriteCellStyleList = contentWriteCellStyleList;}// 构造函数二:接收两个WriteCellStyle对象// 第一个参数是表头部分的单元格的样式// 第二个参数是内容部分的单元格样式public HorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) { this.headWriteCellStyle = headWriteCellStyle; if (contentWriteCellStyle != null) { this.contentWriteCellStyleList = ListUtils.newArrayList(new WriteCellStyle[]{contentWriteCellStyle}); }}WriteCellStyle
其实只是一个单元格的对象。把它交由
HorizontalCellStyleStrategy
之后,就可以被渲染成一行的对象。然后每行都按这个策略执行。接收
List
参数时,会循环渲染集合中的样式对象。 -
AbstractVerticalCellStyleStrategy
:每一列的样式都一样 需要自己回调每一页它是一个抽象类,需要自己定义类去继承它,然后重写里面对应的方法。
这部分文档中的描述几乎没有,全靠自己摸索,可能官方更推荐第一种方式
2.5 使用默认的拦截器HorizontalCellStyleStrategy自定义样式
// 完整代码@Testpublic void handlerStyleWrite() { // 创建一个写出的单元格样式对象 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND // 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // 设置内容背景色为绿色 contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); // 边框设置 contentWriteCellStyle.setBorderTop(BorderStyle.THIN);// 设置单元格上边框为细线 contentWriteCellStyle.setBorderBottom(BorderStyle.THICK);// 设置单元格下边框为粗线 contentWriteCellStyle.setBorderLeft(BorderStyle.MEDIUM); // 设置单元格左边框为中线 contentWriteCellStyle.setBorderRight(BorderStyle.MEDIUM_DASHED);//设置单元格右边框为中虚线 // 创建写出Excel的字体对象 WriteFont contentWriteFont = new WriteFont(); contentWriteFont.setFontHeightInPoints((short)20); //设置字体大小 contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);//设置文字居中 contentWriteCellStyle.setWriteFont(contentWriteFont); // 把字体对象设置到单元格样式对象中 // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, DemoData.class) .registerWriteHandler(horizontalCellStyleStrategy) .sheet("horizontalCellStyleStrategy拦截器设置样式") .doWrite(data());}
2.5 使用默认的拦截器AbstractVerticalCellStyleStrategy自定义样式
1)创建一个类实现AbstractVerticalCellStyleStrategy
public class CustomVerticalCellStyleStrategy extends AbstractVerticalCellStyleStrategy { // 重写定义表头样式的方法 @Override protected WriteCellStyle headCellStyle(Head head) { WriteCellStyle writeCellStyle = new WriteCellStyle(); writeCellStyle.setFillBackgroundColor(IndexedColors.RED.getIndex()); WriteFont writeFont = new WriteFont(); writeFont.setColor(IndexedColors.RED.getIndex()); writeFont.setBold(false); writeFont.setFontHeightInPoints(Short.valueOf((short)15)); writeCellStyle.setWriteFont(writeFont); return writeCellStyle; } // 重写定义内容部分样式的方法 @Override protected WriteCellStyle contentCellStyle(Head head) { WriteCellStyle writeCellStyle = new WriteCellStyle(); writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); writeCellStyle.setFillBackgroundColor(IndexedColors.GREEN.getIndex()); writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); return writeCellStyle; }}
这个方式在背景色设置方面存在问题,不知道是不是bug,这种方式还是慎用吧
2)具体使用
@Testpublic void handlerStyleWrite() {// 创建拦截器对象 CustomVerticalCellStyleStrategy customVerticalCellStyleStrategy = new CustomVerticalCellStyleStrategy(); // 写出Excel EasyExcel.write(fileName, DemoData.class) .registerWriteHandler(customVerticalCellStyleStrategy) .sheet("horizontalCellStyleStrategy拦截器设置样式") .doWrite(data());}
三、自定义拦截器设置Excel样式
3.1 为什么不使用AbstractCellWriteHandler
老版本中自定义拦截器主要是继承 Easyexcel
的抽象类AbstractCellWriteHandler
控制器。
重写beforeCellCreate
前置处理方法和afterCellDispose
后置处理方法完成对应的方法达到控制单个单元格
样式的效果。但是AbstractCellWriteHandler这个抽象类在3.x版本已经被弃用,所以现在不推荐使用它。
3.2 实现CellWriteHandler接口
前面的两种方式,都只能在代码层面批量设置样式,而不能设置导出Excel
中的一部分单元格样式。
实现这个接口后可以重写afterCellDispose
方法来对单个单元格的样式进行设置。
每个单元格处理完毕之后都会调用它。
虽然文档中说不太推荐,可能是这里代码多了会影响性能,但是这是目前设置单个单元格最好的方式了。
3.1 基础使用
1)定义类实现CellWriteHandler接口,并重写afterCellDispose方法
public class CustomCellWriteStrategy implements CellWriteHandler { // 在单元格处理之后执行 @Override public void afterCellDispose(CellWriteHandlerContext context) { // 当前事件会在 数据设置到poi的cell里面才会回调 // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true if (BooleanUtils.isNotTrue(context.getHead())) { // 获取第一个单元格对象 // 只要不是头 一定会有数据 当然fill(填充)的情况 可能要context.getCellDataList() // 这个需要看模板,因为一个单元格会有多个 WriteCellData WriteCellData<?> cellData = context.getFirstCellData(); // cellData 可以获取样式/数据,也可以直接设置样式/数据,设置后会立即生效 // 这里也需要用cellData去获取样式 // 很重要的一个原因是 WriteCellStyle 和 dataFormatData绑定的 // 简单的说 比如你加了 DateTimeFormat,已经将writeCellStyle里面的dataFormatData改了 // 如果你自己new了一个WriteCellStyle,可能注解的样式就失效了 // 然后getOrCreateStyle 用于返回一个样式,如果为空,则创建一个后返回 // (总之记住用这个方法获取样式即可) WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND // 要不然背景色不会生效 writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // 获取当前单元格的数据,必要的时候可以根据数据来设置单元格的颜色 // 比如当前列为状态列,数据为1是正常,背景色为绿色,反正不正常,背景色设置为红 // 这种需求的实现将变得可能 Object data = cellData.getStringValue(); System.out.println("data: " + data); // 这样样式就设置好了 后面有个FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置 到 cell里面去 所以可以不用管了 } }}
2)写出Excel时注册处理策略
@Testpublic void handlerStyleWrite() {// 创建处理器策略对象 CustomCellWriteStrategy customCellWriteStrategy = new CustomCellWriteStrategy(); // 写出Excel EasyExcel.write(fileName, DemoData.class) .registerWriteHandler(customCellWriteStrategy) .sheet("自定义单个单元格样式演示") .doWrite(data());}
四、设置列宽
有实体类的时候,可以使用注解去设置列宽,但是如果是那种无模型的,又该怎么去设置列宽呢。
官方提供的LongestMatchColumnWidthStyleStrategy
,
4.1 AbstractColumnWidthStyleStrategy
1)基础写法
定义一个类,去继承AbstractColumnWidthStyleStrategy
这个抽象类,并且重写里面的setColumnWidth
方法
public class ExcelWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { @Override protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 使用sheet对象 简单设置 index所对应的列的列宽 Sheet sheet = writeSheetHolder.getSheet(); sheet.setColumnWidth(cell.getColumnIndex(), 5000); }}
每处理一个单元格都会调用一次setColumnWidth
方法,这个方法有两种重载,重写哪一个都行。
2)自适应列宽写法
public class ExcelWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { // 单元格的最大宽度 private static final int MAX_COLUMN_WIDTH = 50; // 缓存(第一个Map的键是sheet的index, 第二个Map的键是列的index, 值是数据长度) private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8); // 重写设置列宽的方法 @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(16); CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap); } // 获取数据长度 Integer columnWidth = this.getLength(cellDataList, cell, isHead); if (columnWidth >= 0) { if (columnWidth > MAX_COLUMN_WIDTH) { columnWidth = MAX_COLUMN_WIDTH; } // 确保一个列的列宽以表头为主,如果表头已经设置了列宽,单元格将会跟随表头的列宽 Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); if (maxColumnWidth == null || columnWidth > maxColumnWidth) { maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); // 如果使用EasyExcel默认表头,那么使用columnWidth * 512 // 如果不使用EasyExcel默认表头,那么使用columnWidth * 256 // 如果是自己定义的字体大小,可以再去测试这个参数常量 writeSheetHolder .getSheet() .setColumnWidth(cell.getColumnIndex(), columnWidth * 512); } } } } private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) { if (isHead) { return cell.getStringCellValue().getBytes().length; } else { WriteCellData 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; } } } }}
可以根据自己的需求自行改造
4.2 写出Excel时注册处理策略
@Testpublic void handlerStyleWrite() {// 创建处理器策略对象 ExcelWidthStyleStrategy excelWidthStyleStrategy = new ExcelWidthStyleStrategy(); // 写出Excel EasyExcel.write(fileName, DemoData.class) .registerWriteHandler(excelWidthStyleStrategy) .sheet("单个单元格列宽设置") .doWrite(data());}
五、EasyExcel其它默认设置
5.1 表头自动合并
EasyExcel.write(response.getOutputStream(), DemoData.class) .automaticMergeHead(false) // 自动合并表头 .sheet("模板") .doWrite(demoData);
若automaticMergeHead
设置为true
,那么对于相邻表格中存在相同内容单元格,easyexcel
会自动将其合并。
它的默认值也是true
。
如果不想使用表头自动合并,就设置为false
即可。
5.2 取消导出Excel的默认风格
EasyExcel.write(response.getOutputStream(), DemoData.class) .useDefaultStyle(false) // 取消导出Excel的默认风格 .sheet("模板") .doWrite(demoData);
easyexcel
的默认风格是最明显的体现,对于表头会显示灰色背景,并且字体会加粗和放大。
如果不想使用这个默认风格,把useDefaultStyle
设置为false
即可。
5.3 是否使用1904日期窗口
EasyExcel.write(response.getOutputStream(), DemoData.class) .use1904windowing(true) // 设置使用1904的时间格式 .sheet("模板") .doWrite(demoData);
EasyExcel
中时间是存储1900
年起的一个双精度浮点数。一般也是使用1900
的时间格式就可以了。
如果有业务想把开始日期改为1904
,就可以设置use1904windowing
为true
即可。
来源地址:https://blog.csdn.net/qq_44749491/article/details/127917454