参考网上的链接:
EasyExcel按模板导出与下载(自定义合并单元格)
https://blog.csdn.net/weixin_44511845/article/details/120290264
EasyExcel(根据条件动态合并单元格的重复数据))
https://blog.csdn.net/Violet_201903027/article/details/105724907
编写模板导出时,某一列单元格合并功能
上面的资料使用的EasyExcel版本是:2.1.7
我使用的版本是:2.2.0-beta2
使用资料代码进行模板导出同时,动态单元格合并
github官方提供了 LoopMergeStrategy 合并策略 在模板导出不生效。因为LoopMergeStrategy extends AbstractRowWriteHandler ,使用 afterRowDispose() 行操作完成后执行合并操作。
但是在模板导出过程中afterRowDispose() 方法没有被触发。
合并策略类:ExcelFillCellMergeStrategy
1 public class ExcelFillCellMergeStrategy implements CellWriteHandler {
2
3 /** 需要进行单元格合并的列数组 **/
4 private int[] mergeColumnIndex;
5 /** 单元格合并从第几行开始 **/
6 private int mergeRowIndex;
7
8 public ExcelFillCellMergeStrategy() {}
9
10 public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
11 this.mergeRowIndex = mergeRowIndex;
12 this.mergeColumnIndex = mergeColumnIndex;
13 }
14 。。。
15 。。。
16
17 @Override
18 public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
19 List<CellData> list, Cell cell, Head head, Integer integer, Boolean isHead) {
20 int curRowIndex = cell.getRowIndex();
21 int curColIndex = cell.getColumnIndex();
22 if (curRowIndex > mergeRowIndex) {
23 for (int i = 0; i < mergeColumnIndex.length; i++) {
24 if (curColIndex == mergeColumnIndex[i]) {
25 mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
26 break;
27 }
28 }
29 }
30 }
31
32 /**
33 * 当前单元格向上合并
34 *
35 * @param writeSheetHolder
36 * @param cell
37 * 当前单元格
38 * @param curRowIndex
39 * 当前行
40 * @param curColIndex
41 * 当前列
42 */
43 private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
44 Object curData =
45 cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
46 Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
47 Object preData =
48 preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
49 // 将当前单元格数据与上一个单元格数据比较
50 Boolean dataBool = preData.equals(curData);
51 if (dataBool) {
52 Sheet sheet = writeSheetHolder.getSheet();
53 List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
54 boolean isMerged = false;
55 for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
56 CellRangeAddress cellRangeAddr = mergeRegions.get(i);
57 // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
58 if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
59 sheet.removeMergedRegion(i);
60 cellRangeAddr.setLastRow(curRowIndex);
61 sheet.addMergedRegion(cellRangeAddr);
62 isMerged = true;
63 }
64 }
65 // 若上一个单元格未被合并,则新增合并单元
66 if (!isMerged) {
67 CellRangeAddress cellRangeAddress =
68 new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
69 sheet.addMergedRegion(cellRangeAddress);
70 }
71 }
72 }
73 }
测试代码
@Test
public void fillTest1() throws IOException {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// 填充list 的时候还要注意 模板中{.} 多了个点 表示list
String templatePath = EasyexcelAnnotationFillTest.class.getResource("/").getPath();
templatePath = templatePath + "fillTemplate.xlsx";
OutputStream outputStream = new FileOutputStream("E:/测试填充数据.xlsx");
// 填充列表数据
List<FillWithAnnotationData> listData = getFillListData();
// 第一列进行单元格合并
int[] mergeColumeIndex = {0};
// 从第4行开始合并
int mergeRowIndex = 3;
ExcelWriter excelWriter = EasyExcelFactory.write(outputStream)
.withTemplate(templatePath)
//设置合并单元格策略
.registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex))
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
excelWriter.fill(listData, writeSheet);
excelWriter.finish();
outputStream.close();
}
public List<FillWithAnnotationData> getFillListData() {
List<FillWithAnnotationData> listData = new ArrayList<>();
for (int i=0; i < 10; i++) {
FillWithAnnotationData data = new FillWithAnnotationData();
data.setName("名称");
data.setMoney(new BigDecimal(1002.35+i).toPlainString());
data.setNumber(1002.35+i);
listData.add(data);
}
return listData;
}
public class FillWithAnnotationData {
private String name;
@NumberFormat(",##0.00")
@ExcelFillProperty(converter = StringNumberConverter.class)
private String money;
@NumberFormat(",##0.00")
@ExcelFillProperty(converter = DoubleStringConverter.class)
private Double number;
}
测试模板如下图
报错位置:ExcelFillCellMergeStrategy合并策略类的 mergeWithPrevRow()方法中
第46行位置
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
这一行代码会报空指针异常 java.lang.NullPointerException
原因:
debug发现,cell.getSheet() 行的下标第0到3的数据行,获取的是同一个 sheet 实例
当下标为4时,执行cell.getSheet()获取到的 sheet 实例不一样,而且里面的sheet存在的row数据,只有下标为4以后的
而下标0到3的行数据被存储到 存储sheet中。
writeSheetHolder.getCachedSheet()
所以改进合并策略类的合并方法:
当获取不到前一行数据时,查找缓存sheet中的行数据
/**
* 当前单元格向上合并
*
* @param writeSheetHolder
* @param cell
* 当前单元格
* @param curRowIndex
* 当前行
* @param curColIndex
* 当前列
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
Object curData =
cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
Row preRow = cell.getSheet().getRow(curRowIndex - 1);
if (preRow == null) {
// 当获取不到上一行数据时,使用缓存sheet中数据
preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1);
}
Cell preCell = preRow.getCell(curColIndex);
Object preData =
preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
// 将当前单元格数据与上一个单元格数据比较
Boolean dataBool = preData.equals(curData);
if (dataBool) {
Sheet sheet = writeSheetHolder.getSheet();
List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
boolean isMerged = false;
for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
CellRangeAddress cellRangeAddr = mergeRegions.get(i);
// 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
sheet.removeMergedRegion(i);
cellRangeAddr.setLastRow(curRowIndex);
sheet.addMergedRegion(cellRangeAddr);
isMerged = true;
}
}
// 若上一个单元格未被合并,则新增合并单元
if (!isMerged) {
CellRangeAddress cellRangeAddress =
new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellRangeAddress);
}
}
}