easyexcel官方文档中可以设置表头单元格格式及内容的单元格格式,其中单元格格式策略类中有两个构造函数
public HorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle,
List<WriteCellStyle> contentWriteCellStyleList) {
this.headWriteCellStyle = headWriteCellStyle;
this.contentWriteCellStyleList = contentWriteCellStyleList;
}
public HorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {
this.headWriteCellStyle = headWriteCellStyle;
contentWriteCellStyleList = new ArrayList<WriteCellStyle>();
contentWriteCellStyleList.add(contentWriteCellStyle);
}
两者的不同之处在于:第一个构造函数给内容设置单元格格式参数为一个集合,通过下方源码可以看到,
HorizontalCellStyleStrategy类中给内容设置单元格格式的代码如下:
@Override
protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) {
if (contentCellStyleList == null || contentCellStyleList.isEmpty()) {
return;
}
cell.setCellStyle(contentCellStyleList.get(relativeRowIndex % contentCellStyleList.size()));
}
可以看出contentCellStyleList就是构造函数中内容单元格格式集合,该集合作用其实是给excel隔行换样式,比如你给了2种不同的样式,则1,3,5行为一个样式,2,4,6行为另一种样式,比如你给了3种不同的样式,则1,4,7行为一个样式,2,5,8行为另一种样式,3,6,9行为一种样式,通过cell.setCellStyle(contentCellStyleList.get(relativeRowIndex % contentCellStyleList.size()))这句代码就可以看出。
通过上述代码我们发现虽然easyexcel是通过给行设置样式,即同一行中,不同列样式是一样的,但一行中的每一列都会执行上述代码setContentCellStyle方法,这就是我们可以重写的地方,我们可以通过重写该方法,实现自己对单元格格式的精准控制
实现方式如下:
通过调试发现,该方法中的head参数是可以拿到单元格对应的实体类的参数名,即field的name,如下方的remark参数
@ExcelProperty("备注")
private String remark;
既然可以取到field的name,那么就可以自由发挥了。
我第一版实现是通过field的name中有特定字符串,然后设置单独样式,比如下面就是如果field的name中以amount结尾,就执行我设置的单元格格式,其它的执行公共的内容单元格格式。
private static final String AMOUNT = "amount";
@Override
protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) {
if (head.getFieldName().toLowerCase().endsWith(AMOUNT)) {
Workbook wb = cell.getSheet().getWorkbook();
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 13);
cellStyle.setFont(font);
cellStyle.setAlignment(HorizontalAlignment.RIGHT);
cell.setCellStyle(cellStyle);
}else {
super.setContentCellStyle(cell,head,relativeRowIndex);
}
}
上述实现有一个最大的局限就是,如果同一行中,有各种各样的格式,则使用起来特别麻烦。所以就有了第二种方式:通过自定义注解的方式实现。
因为我们可以获取到field的name,那么就会想到反射,只要给到field的name的class,就可以获取到Field。通过反射获取到field上的注解,给field设置单元格格式,实现代码如下:
@Slf4j
public class ExcelStyleAnnotationCellWriteHandler extends HorizontalCellStyleStrategy {
private Class c;
ExcelStyleAnnotationCellWriteHandler(Class c, WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {
super(headWriteCellStyle, contentWriteCellStyle);
this.c = c;
}
@Override
protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) {
try {
Field declaredField = c.getDeclaredField(head.getFieldName());
ExcelStyle annotation = declaredField.getAnnotation(ExcelStyle.class);
if (annotation != null) {
Workbook wb = cell.getSheet().getWorkbook();
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName(annotation.fontName());
font.setFontHeightInPoints(annotation.fontHeightInPoints());
cellStyle.setFont(font);
cellStyle.setAlignment(annotation.horizontalAlignment());
cellStyle.setVerticalAlignment(annotation.verticalAlignment());
cell.setCellStyle(cellStyle);
}else {
super.setContentCellStyle(cell,head,relativeRowIndex);
}
} catch (NoSuchFieldException e) {
log.error("ExcelStyleAnnotationCellWriteHandler error{0}",e);
}
}
}
自定义注解如下,写的不全,大家可以自行补充
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelStyle {
String fontName() default "宋体";
short fontHeightInPoints() default 12;
HorizontalAlignment horizontalAlignment() default HorizontalAlignment.LEFT;
VerticalAlignment verticalAlignment() default VerticalAlignment.CENTER;
}
注解使用方式如下
@ExcelProperty("账号")
@ColumnWidth(28)
@ExcelStyle(fontName = "宋体",horizontalAlignment = HorizontalAlignment.LEFT)
private String account;
通过重写setContentCellStyle方法,读取到field上的注解,给单元格设置格式,没有注解的,走通用的样式。
使用多行表头时遇到的一点问题:https://github.com/alibaba/easyexcel/issues/990,大家可以看下,避免坑到自己