该图片由birgl在Pixabay上发布

你好,我是看山。

前文 聊了 EasyExcel 的内容导出,本文主要说一下导出文件的格式化,格式化包括工作表/单元格样式和内容格式化。毕竟,有时候还是要看脸。

内容比较多,文内只会列出关键代码,想要完整源码,可以关注公号「看山的小屋」回复“easyexcel”获取。

注解格式

通过注解定义格式是 EasyExcel 封装的高级功能,可以让我们很方便的定义格式。

格式化内容

先定义一个使用注解格式化内容的实体类:

@Data
public class FormatContentItem {@ExcelProperty(value = "字符串标题", converter = TitleFormatConverter.class)private String string;@DateTimeFormat("yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒")@ExcelProperty(value = "日期标题")private Date date;@NumberFormat("0.000%")@ExcelProperty("数字标题")private Double doubleData;
}

其中DateTimeFormatNumberFormat两个注解都是自带的注解,用于格式化时间和数字。

DateTimeFormat注解有两个属性,一个属性是value,用来定义时间格式,可以参考java.text.SimpleDateFormat;另一个属性是use1904windowing,表示使用时间使用 1904 时间系统还是 1900 时间系统,默认是否。

NumberFormat注解有两个属性,一个属性是value,用来定义数字格式,可以参考java.text.DecimalFormat;另一个属性是roundingMode,用来定义保留小数的方式,使用的是java.math.RoundingMode枚举。

想要格式化字符串,可以借助ExcelProperty的 converter 属性,这个属性传入实现Converter的类。比如示例中的TitleFormatConverter,代码如下:

public class TitleFormatConverter implements Converter<String> {@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,GlobalConfiguration globalConfiguration) {return new WriteCellData<>(String.format("标题:%s(自定义)", value));}}

结果为:

定义行高、列宽

使用注解定义行高的话,可以使用HeadRowHeight定义表头高度,使用ContentRowHeight定义表体高度,这个注解定义之后,所有表体高度都是相同的。列宽可以使用ColumnWidth注解定义,这个注解可以定义在类上,表示整个表格的列都一样宽,也可以定义的属性上,表示指定列的宽度。

@Data
@HeadRowHeight(20)
@ContentRowHeight(10)
@ColumnWidth(25)
public class FormatCellItem {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ColumnWidth(50)@ExcelProperty("数字标题")private Double doubleData;
}

结果为:

单元格定义样式

控制单元格样式有四个注解:HeadStyleHeadFontStyleContentStyleContentFontStyle,这四个注解可以定义在类上作为全局表格的样式,也可以定义在字段上,作为当前列的样式。下面分别说一下这几个注解中比较常用的配置。

  • *Style:分为HeadStyleContentStyle,分别定义表头和表体样式

    • dataFormat:表头格式化,short 格式,是org.apache.poi.ss.usermodel.BuiltinFormats类中已定义格式的小标
    • border*:分别是 borderLeft、borderRight、borderTop、borderBottom 四个属性,类型是com.alibaba.excel.enums.poi.BorderStyleEnum枚举,用来定义表头单元格边框样式。边框的颜色也可以定义,使用、*BorderColor 定义即可。
    • fillPatternType:填充类型,类型是com.alibaba.excel.enums.poi.FillPatternTypeEnum枚举,如果想要填充背景色,这个属性需要设置为SOLID_FOREGROUND
    • fillForegroundColor:前景色,类型是 short,值却是使用的org.apache.poi.ss.usermodel.IndexedColors枚举的 idx 值,只不过,两个类型不一致,一个是 short,一个是 int,没有办法直接引用。可见 java 中的依赖之间,还是有很多坑的。
    • fillBackgroundColor:背景色,同fillForegroundColor
    • rotation:内容旋转角度
  • *FontStyle:有HeadFontStyleContentFontStyle,分别定义表头和表体的字体样式。
    • fontName:定义字体名称,类型字符串
    • fontHeightInPoints:字号大小,类型是 short
    • italic:是否斜体,类型是com.alibaba.excel.enums.BooleanEnum
    • bold:是否加粗,类型是com.alibaba.excel.enums.BooleanEnum
    • strikeout:是否使用删除线(这个词本意是三振出局的意思,应该是与棒球有关)
    • color:文本颜色,值使用的是org.apache.poi.ss.usermodel.IndexedColors,依然有类型不一致的情况
    • underline:下划线,类型是 byte,可以直接使用Font.U_NONEFont.U_SINGLEFont.U_DOUBLEFont.U_SINGLE_ACCOUNTINGFont.U_DOUBLE_ACCOUNTING

我们可以这么定义:

@Data
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成 20
@HeadFontStyle(fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成 20
@ContentFontStyle(fontHeightInPoints = 20)
public class FormatStyleCellItem {// 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)// 字符串的头字体设置成 20@HeadFontStyle(fontHeightInPoints = 30)// 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)// 字符串的内容字体设置成 20@ContentFontStyle(fontHeightInPoints = 30)@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;
}

结果为:

类对象定义格式

这种方式可以说是纯手工组装数据了,使用的是com.alibaba.excel.metadata.data.WriteCellData类,这个类相当于是单元格的定义,通过设置com.alibaba.excel.enums.CellDataTypeEnum枚举类型的 type 属性,可以指明当前单元格格式。

守恒定律一直存在。这种方式灵活度很高,可以精细到具体的单元格格式,但是繁琐程度也增加了。

超链接

超链接使用的是com.alibaba.excel.metadata.data.HyperlinkData类,需要设置地址、超链类型(com.alibaba.excel.metadata.data.HyperlinkData.HyperlinkType枚举),然后将值写入到WriteCellData对象的hyperlinkData属性即可。

// 设置超链接
HyperlinkData hyperlinkData = new HyperlinkData();
hyperlinkData.setAddress("https://www.howardliu.cn");
hyperlinkData.setHyperlinkType(HyperlinkType.URL);
WriteCellData<String> hyperlink = new WriteCellData<>("网站");
hyperlink.setHyperlinkData(hyperlinkData);

备注

备注使用的是com.alibaba.excel.metadata.data.CommentData类,需要设置作者、备注内容(com.alibaba.excel.metadata.data.RichTextStringData类型),因为备注的默认大小是单元格大小,如果感觉太小,还可以设置相对高度和宽度。

// 设置备注
CommentData commentData = new CommentData();
commentData.setAuthor("Howard Liu");
commentData.setRichTextStringData(new RichTextStringData("这是一个备注"));
// 备注的默认大小是按照单元格的大小 这里想调整到 4 个单元格那么大 所以向后 向下 各额外占用了一个单元格
commentData.setRelativeLastColumnIndex(1);
commentData.setRelativeLastRowIndex(1);
WriteCellData<String> comment = new WriteCellData<>("备注的单元格信息");
comment.setCommentData(commentData);

公式

公式使用的是com.alibaba.excel.metadata.data.FormulaData类,可以直接设置formulaValue公式,不过官方不太推荐使用公式。

// 设置公式
FormulaData formulaData = new FormulaData();
// 将 123456789 中的第一个数字替换成 2
// 这里只是例子 如果真的涉及到公式 能内存算好尽量内存算好 公式能不用尽量不用
formulaData.setFormulaValue("REPLACE(123456789,1,1,2)");
WriteCellData<String> formula = new WriteCellData<>();
formula.setFormulaData(formulaData);

单元格格式

通过类定义单元格格式,与通过注解定义本质是一样的。所以与注解HeadStyleHeadFontStyleContentStyleContentFontStyle对应,设置单元格格式的类是WriteCellStyle,设置字体的类是WriteFont。其中这些类的属性与注解的也是类似,不再赘述太多,直接上例子。(其实我觉得使用类定义格式的场景不多,真的碰到了,看看类定义就明白了)

// 设置单个单元格的样式 当然样式 很多的话 也可以用注解等方式。
WriteCellStyle writeCellStyleData = new WriteCellStyle();
// 这里需要指定 FillPatternType 为 FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色。
writeCellStyleData.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 背景绿色
writeCellStyleData.setFillForegroundColor(IndexedColors.GREEN.getIndex());
WriteCellData<String> writeCellStyle = new WriteCellData<>("单元格样式");
writeCellStyle.setWriteCellStyle(writeCellStyleData);
writeCellStyle.setType(CellDataTypeEnum.STRING);// 设置单个单元格多种样式
RichTextStringData richTextStringData = new RichTextStringData();
richTextStringData.setTextString("红色绿色默认");
// 前 2 个字红色
WriteFont writeFont = new WriteFont();
writeFont.setColor(IndexedColors.RED.getIndex());
richTextStringData.applyFont(0, 2, writeFont);
// 接下来 2 个字绿色
writeFont = new WriteFont();
writeFont.setColor(IndexedColors.GREEN.getIndex());
richTextStringData.applyFont(2, 4, writeFont);
WriteCellData<String> richTest = new WriteCellData<>();
richTest.setType(CellDataTypeEnum.RICH_TEXT_STRING);
richTest.setRichTextStringDataValue(richTextStringData);

结果为:

拦截器定义格式

除了直接使用类定义格式,我们还可以借助拦截器实现。(这里在名称上会有一些歧义,所用的类对象命名都是 xxxStrategy,翻译过来就是 xxx 策略,但是官方对其命名为拦截器)

已有拦截器

前面示例中使用WriteCellStyleWriteFont可以实现单元格的样式,如果想要实现整行数据都是相同的格式,可以借助com.alibaba.excel.write.style.HorizontalCellStyleStrategy拦截器。

/*** 使用已有策略实现自定义样式** <ul>*     <li>HorizontalCellStyleStrategy 每一行的样式都一样 或者隔行一样</li>*     <li>AbstractVerticalCellStyleStrategy 每一列的样式都一样 需要自己回调每一页</li>* </ul>*/
private static void writeByCellStyleStrategy() {String fileName = defaultFileName("writeByCellStyleStrategy");// 表头策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为红色headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());WriteFont headWriteFont = new WriteFont();headWriteFont.setFontHeightInPoints((short) 40);headWriteCellStyle.setWriteFont(headWriteFont);// 表体策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 这里需要指定 FillPatternType 为 FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色。表头默认了 FillPatternType 所以可以不指定contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);// 背景绿色contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());WriteFont contentWriteFont = new WriteFont();// 字体大小contentWriteFont.setFontHeightInPoints((short) 20);contentWriteCellStyle.setWriteFont(contentWriteFont);// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);// 这里 需要指定写用哪个 class 去写,然后写到第一个 sheet,名字为模板 然后文件流会自动关闭EasyExcelFactory.write(fileName).head(Item.class).registerWriteHandler(horizontalCellStyleStrategy).sheet().doWrite(sampleItems());
}

结果为:

正如上面的结果,如果我们某个单元格数据比较长,可能会有遮挡,这个时候我们可以使用com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy实现自动列宽调整。不过这个不太精确,但聊胜于无。

private static void writeUseLongestMatchColumnWidthStyleStrategy() {String fileName = defaultFileName("writeUseLongestMatchColumnWidthStyleStrategy");EasyExcelFactory.write(fileName).head(Item.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet().doWrite(sampleItems());
}

结果为:

可以看到,确实不够精确。

自定义拦截器

上面展示的拦截器,都是实现了com.alibaba.excel.write.handler.WriteHandler接口,然后使用com.alibaba.excel.write.builder.AbstractExcelWriterParameterBuilder.registerWriteHandler方法注册到写函数中。所以,我们也可能根据需要,自己定义需要的拦截器。

这种自定义拦截器属于低级功能,需要了解很多底层设计和 API,鉴于篇幅,本文没有办法覆盖,这里只给出例子。如果有需要,可以留言沟通。

比如,我们需要某些单元格设置数据验证,展现形式就是下拉菜单,我们可以这样写:

public class ColumnValidationWriteHandler implements SheetWriteHandler {@Overridepublic void afterSheetCreate(SheetWriteHandlerContext context) {// 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0);DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试 1", "测试 2"});DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);}
}

如果我们需要将某个单元格的格式设置为超链,也可以使用拦截器:

public class CellStyleWriteHandler implements CellWriteHandler {@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {Cell cell = context.getCell();// 这里可以对 cell 进行任何操作if (BooleanUtils.isTrue(context.getHead()) && cell.getColumnIndex() == 0) {CreationHelper createHelper = context.getWriteSheetHolder().getSheet().getWorkbook().getCreationHelper();Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);hyperlink.setAddress("https://www.howardliu.cn");cell.setHyperlink(hyperlink);}}
}

结果为:

合并单元格

EasyExcel 提供的合并单元格功能比较简单,有两种方式:基于注解的合并、基于拦截器的合并。

注解

基于注解的合并单元格提供了两个注解:

  • OnceAbsoluteMerge注解实现指定位置的合并
  • ContentLoopMerge这个是内容的循环合并,指定某一列每几行合并。
// 将第 6-7 行的 2-3 列合并成一个单元格
@OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)
@Data
public class MergeCellItem {@ContentLoopMerge(eachRow = 2)@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;
}

结果为:

拦截器

拦截器合并也是有两种,对应着注解:

  • OnceAbsoluteMergeStrategy,相对位置合并
  • LoopMergeStrategy循环合并
private static void writeMergeCellCustom() {String fileName = defaultFileName("writeMergeCellCustom");// 每隔 2 行会合并// 把 eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);EasyExcelFactory.write(fileName).head(Item.class).registerWriteHandler(loopMergeStrategy).sheet().doWrite(sampleItems());
}

结果为:

文末总结

本文从实战角度说了一下 EasyExcel 如果实现写出好看的表格,EasyExcel中提供了很多用于格式化的注解、拦截器,可以时薪通用的格式化输出,如果还有更加个性化的格式要求,也可以自定义拦截器实现。接下来聊一下如何填充模板。

推荐阅读

  • 阿里开源的这个库,让 Excel 导出不再复杂(简简单单的写)
  • 阿里开源的这个库,让 Excel 导出不再复杂(既要能写,还要写的好看)

你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请点赞、收藏、关注。我还整理了一些精品学习资料,关注公众号「看山的小屋」,回复“资料”即可获得。

个人主页:https://www.howardliu.cn
个人博文:阿里开源的这个库,让 Excel 导出不再复杂(既要能写,还要写的好看)
CSDN 主页:https://kanshan.blog.csdn.net/
CSDN 博文:阿里开源的这个库,让 Excel 导出不再复杂(既要能写,还要写的好看)

阿里开源的这个库,让 Excel 导出不再复杂(既要能写,还要写的好看)相关推荐

  1. 阿里开源的这个库,让 Excel 导出不再复杂(简简单单的写)

    该图片由dae jeung kim在Pixabay上发布 你好,我是看山. 导出是中后台常见的功能,Excel文件是常见的导出格式. 在Java栈中,常用的是JXL(目前改名为JExcel)和Apac ...

  2. 阿里开源(EasyExcel)---导入EXCEL

    一.简介 二.案例 2.1 POM依赖 <!-- 阿里开源EXCEL --><dependency><groupId>com.alibaba</groupId ...

  3. vue--echarts 图标库、excel导出、面包屑组件、富文本框、地图、前端使用代理访问、监控生产环境or开发环境

    目录 一.echarts 图标库 1.echarts的基础 2.项目中的使用 二.execl导出 三.面包屑组件 四.富文本框 五.地图 六.vite 构建配置 七.后端未开跨域资源共享,前端使用代理 ...

  4. POI百万级大数据量EXCEL导出

    一. 简介 excel导出,如果数据量在百万级,会出现俩点内存溢出的问题: 1. 查询数据量过大,导致内存溢出. 该问题可以通过分批查询来解决: 2. 最后下载的时候大EXCEL转换的输出流内存溢出: ...

  5. 阿里开源 EasyExcel(Excel导入导出 推荐使用)

    二.阿里开源 EasyExcel 使用可以直接看(点击)官方文档 github地址:(https://github.com/alibaba/easyexcel Java解析.生成Excel比较有名的框 ...

  6. 阿里开源(EasyExcel)---导出EXCEL

    一. 简介 导出是后台管理系统的常用功能,当数据量特别大的时候会内存溢出和卡顿页面,曾经自己封装过一个导出,POI百万级大数据量EXCEL导出 采用了分批查询数据来避免内存溢出和使用SXSSFWork ...

  7. 阿里开源(EasyExcel):使用Java将数据导出为Excel表格、带样式----》java web下载 Excel文件

    目录 一.技术选型 二.实现过程 1.导入依赖 2.编写工具类 EasyExcelUtil 3.公用参数类 EasyExcelParams 4.表格样式实体类 MyWriteHandler 5.数据实 ...

  8. 阿里开源百万级数据导出Excel表格 三步简单导出 附官方文档

    阿里巴巴Excel导出优化速度 ,64M内存20秒读取75M(46W行25列)的Excel(3.0.2+版本) 官方文档:EasyExcel · 语雀EasyExcel是一个基于Java的简单.省内存 ...

  9. C#底层库--操作Excel帮助类(读取、导出表格)

    系列文章 C#底层库–记录日志帮助类 本文链接:https://blog.csdn.net/youcheng_ge/article/details/124187709 C#底层库–MySQL脚本自动构 ...

  10. 阿里开源库vLayout的使用

    vlayout是阿里开源的一个用来辅助RecyclerView的LayoutManager扩展库,通过使用里面的LayoutHelper,我们可以轻松的使用一些比较复杂的布局,如:线性布局.Grid布 ...

最新文章

  1. 【C++】多线程与原子操作和无锁编程【五】
  2. java常见不可变类_Java语言不可变类的诀窍
  3. spss如何选择需要的变量?
  4. Web项目之网络爬虫
  5. webp批量转换jpg_转换文件格式快人一步!右键菜单直接转换,支持图片、文档、视频等……...
  6. Cocos Creator 粒子效果插件
  7. 电路计算机辅助设计上海电力学院,上海电力学院电路计算机辅助设计1.doc
  8. 背包问题1:【SSL】1059.01背包问题——2021-03-10更
  9. SharePoint Designer定制MOSS/WSS表单页面
  10. 工业循环水过滤浅层介质过滤器(浅层砂过滤器)介绍
  11. 微信停止为苹果服务器,苹果手机终于解决了微信延迟
  12. SCI 期刊、SCIE 期刊和 ESCI 期刊的比较
  13. 【简易教程】使用3dmax利用网上免费人物模型来制作人物动作动画
  14. 【向生活低头】十分白痴地自动删微博文章脚本
  15. CHM:新生儿肠道微生物菌群研究
  16. kobject(转载)
  17. unity3d 模拟电脑实现_基于Unity3D的机器人仿真实验系统
  18. Echart实现折线图
  19. uname -a详细解释
  20. 心理学与计算机交叉学,认知心理学其与邻近学科交叉产物

热门文章

  1. Python基础入门实验3附加题
  2. DlhSoft Kanban,WPF 的看板组件
  3. 大白菜备份还原linux,大白菜pe手动gho镜像还原,大白菜制作ghost镜像
  4. 立方尾不变-Python
  5. vue项目为什么选择svg图标
  6. 构建具有丰富功能的交互式 Kiosk
  7. linux内核修改电流,为AM335x移植Linux内核主线代码(43)USB HUB和鼠标
  8. CF736 D1 格点图,皮克定理
  9. ocpc php,oCPC匹配词很乱怎么办?| SEM问答
  10. 知网靠论文一年收费10多亿