利用easyexcel生成excel文件-复杂表头/多表头/自定义表头设计
By:Roy.LiuLast updated:2020-12-02
在上一篇文章( http://www.yihaomen.com/article/1850.html ) 测试了easyexcel生成简单的EXCEL文件,继续测试利用easyexcel生成复杂表头,或者自定义表头的设计。
既然是多表头,复杂的自定义表头设计,原来利用注解来实现单元格的宽度,样式等都不能用了,这个时候需要自定义指定参数,测试还是用上一篇文章提到的订单来做,最终实现的效果如下:
先上代码,然后在稍稍解释一下:
package com.yihaomen.myexcel; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy; import com.yihaomen.myexcel.domain.Detail; import com.yihaomen.myexcel.domain.Master; import org.apache.poi.ss.usermodel.*; import org.junit.Before; import org.junit.Test; import org.springframework.boot.test.context.SpringBootTest; import sun.security.x509.CertAttrSet; import java.io.File; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; @SpringBootTest public class WriteExcel_1 { private Master master; private List<Detail> details = new ArrayList<>(); /** * 初始化测试数据 */ @Before public void initData() { master = new Master(); master.setCustomer("特朗普集团"); master.setSheetNo("SO2020000011111"); master.setCreatedDate(new Date()); for(int i=0; i<100; i++) { Detail d = new Detail(); d.setProductId("product:" + i); d.setProductName("产品:" + i); d.setPrice(new BigDecimal("26.80")); d.setNumber(new BigDecimal("20.5")); d.setAmount(d.getPrice().multiply(d.getNumber())); d.setMemo("备注产品:" + i); details.add(d); } } /** * 仅仅输出明细数据到excel文件 */ @Test public void writeSimpleExcelForDetail() { String path = this.getClass().getResource("/").getPath(); System.out.println(path); String fileName = path + File.separator + "writeSimpleExcel.xlsx"; // 这里 需要指定写用哪个class去写,指定模板名称及数据 EasyExcel.write(fileName, Detail.class).sheet("sheet名称").doWrite(details); } /** * 仅仅输出明细数据到excel文件, 排除部分字段 */ @Test public void writeSimpleExcelExculdeColumns() { String path = this.getClass().getResource("/").getPath(); List<String> excludeColumns = new ArrayList<>(); excludeColumns.add("memo"); String fileName = path + File.separator + "writeSimpleExcel.xlsx"; // 这里 需要指定写用哪个class去写,指定模板名称及数据 EasyExcel.write(fileName, Detail.class).excludeColumnFiledNames(excludeColumns).sheet("sheet名称").doWrite(details); } /** * 仅仅输出明细数据到excel文件, 增加自定义的头部 */ @Test public void writeSimpleExcelWithHeader() { String path = this.getClass().getResource("/").getPath(); String fileName = path + File.separator + "writeSimpleExcel.xlsx"; // 准备 HEADER List<List<String>> list = getHeader(); // 这里 需要指定写用哪个class去写,指定模板名称及数据 EasyExcel.write(fileName).head(list).registerWriteHandler(new CustomizeColumnWidth()) .registerWriteHandler(getStyleStrategy()).sheet("sheet名称").doWrite(details); } private List<List<String>> getHeader() { /** * 打算展示成如下样子 * |客户:xxx 公司 (这一行需要合并单元格) * |单号: SO22222222222222| 日期: 2020-01-01 (分别需要合并单元格) * |产品ID|产品名称|价格|数量|总金额|备注| */ String customer = "客户: " + master.getCustomer(); String sheetNo = "单号: " + master.getSheetNo(); String dateStr = "日期: " + master.getCreatedDate(); List<List<String>> list = new ArrayList<List<String>>(); List<String> head0 = new ArrayList<String>(); head0.add(customer); head0.add(sheetNo); head0.add("产品ID"); List<String> head1 = new ArrayList<String>(); head1.add(customer); head1.add(sheetNo); head1.add("产品名称"); List<String> head2 = new ArrayList<String>(); head2.add(customer); head2.add(sheetNo); head2.add("价格"); List<String> head3 = new ArrayList<String>(); head3.add(customer); head3.add(sheetNo); head3.add("数量"); List<String> head4 = new ArrayList<String>(); head4.add(customer); head4.add(dateStr); head4.add("总金额"); List<String> head5 = new ArrayList<String>(); head5.add(customer); head5.add(dateStr); head5.add("备注"); list.add(head0); list.add(head1); list.add(head2); list.add(head3); list.add(head4); list.add(head5); return list; } private HorizontalCellStyleStrategy getStyleStrategy() { // 头的策略 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 设置对齐 headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT); // 背景色, 设置为白色,也是默认颜色 headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex()); // 字体 WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 12); headWriteCellStyle.setWriteFont(headWriteFont); // 内容的策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 // contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); // 背景绿色 //contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); // 字体策略 WriteFont contentWriteFont = new WriteFont(); contentWriteFont.setFontHeightInPoints((short) 12); contentWriteCellStyle.setWriteFont(contentWriteFont); //设置 自动换行 contentWriteCellStyle.setWrapped(true); //设置 垂直居中 contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //设置 水平居中 // contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); //设置边框样式 contentWriteCellStyle.setBorderLeft(BorderStyle.THIN); contentWriteCellStyle.setBorderTop(BorderStyle.THIN); contentWriteCellStyle.setBorderRight(BorderStyle.THIN); contentWriteCellStyle.setBorderBottom(BorderStyle.THIN); // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); return horizontalCellStyleStrategy; } /** * 自定义头部的 列的宽度设置 策略. . */ class CustomizeColumnWidth extends AbstractColumnWidthStyleStrategy { @Override protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean isHead) { // 测试为 COLUMN 宽度定制. if (isHead && cell.getRowIndex() == 2) { int columnWidth = cell.getStringCellValue().getBytes().length; int cellIndex = cell.getColumnIndex(); switch (cellIndex) { case 0: case 2: case 3: columnWidth = 10; break; case 1: columnWidth = 25; break; case 4: columnWidth = 15; break; case 5: columnWidth = 50; break; default: break; } if (columnWidth > 255) { columnWidth = 255; } writeSheetHolder.getSheet().setColumnWidth(cellIndex, columnWidth * 256); } } @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { // 设置行高测试 int rowIndex = row.getRowNum(); System.out.println("当前行: " + rowIndex); short height = 600; row.setHeight(height); } } }
测试方式是 writeSimpleExcelWithHeader, 其中通过 getHeader() 方法去得到自定义表头, 注意自定义表头合并部分的写法, 另外,自定义表头样式是通过 getStyleStrategy()方法得到的,头部样式与内容样式是分开设置的。另外对于行高,列框是通过实现了easyexcel 里面提供的方法实现的, 也就是自定义类 CustomizeColumnWidth .
最终生成excel的部分代码,只有少许差别:
EasyExcel.write(fileName).head(list).registerWriteHandler(new CustomizeColumnWidth()) .registerWriteHandler(getStyleStrategy()).sheet("sheet名称").doWrite(details);
其实里面还是用的POI的方法,通过实现 WriteHandler可以实现很多自己想要的功能。
From:一号门
COMMENTS