一、概述
虽然EasyExcel
已经提供了一系列注解方式去设置样式。
但是如果没有实体类,或者想要更精确的去设置导出文件的Excel
样式的时候就需要在代码层面去控制样式了。
二、使用已有拦截器自定义样式
主要步骤:
- 创建
Excel
对应的实体对象 - 创建一个
style
策略 并注册 - 写出
Excel
第一步是否需要创建Excel实体对象,得根据实际需求而定,如果导出字段不固定则使用无模型的方式即可
不使用实体类时可以直接传入List<List<
String
>>类型的数据来作为表头和数据内容。
2.1 定义一个Excel实体类
@Getter
@Setter
@EqualsAndHashCode
public 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); // 设置字体大小为20
headWriteFont.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<WriteCellStyle>集合 // 第一个参数是表头部分单元格的样式 // 第二个参数是内容部分的单元格样式 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<WriteCellStyle>
参数时,会循环渲染集合中的样式对象。 -
AbstractVerticalCellStyleStrategy
:每一列的样式都一样 需要自己回调每一页它是一个抽象类,需要自己定义类去继承它,然后重写里面对应的方法。
这部分文档中的描述几乎没有,全靠自己摸索,可能官方更推荐第一种方式
2.5 使用默认的拦截器HorizontalCellStyleStrategy自定义样式
// 完整代码
@Test
public 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)具体使用
@Test
public 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时注册处理策略
@Test
public 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);
}
}
}
}
/**
* 获取当前单元格的数据长度
* @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 {
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时注册处理策略
@Test
public 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://www.bmabk.com/index.php/post/116460.html