官方默认提供了两个单元格合并策略OnceAbsoluteMerge
及LoopMergeStrategy
。
OnceAbsoluteMerge
只能实现一次合并,如果需要多次合并,则需要注册多次服务。
LoopMergeStrategy
可以实现循环合并,但是只能实现每隔 n 个单元格合并。
如图,还有另外一个静态类 AbstractMergeStrategy
是两者的父类,可以据此,自己实现合并方案。
合并策略
package cn.com.hmsm.bif.xingwen.accounting;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.List;
/**
* 指定列,如果相邻两行的数据相同,则合并为一行
*/
@Slf4j
public class ColumnSameDataMergeStrategy extends AbstractMergeStrategy {
private final int mergeRowIndex;
public ColumnSameDataMergeStrategy(int mergeRowIndex) {
this.mergeRowIndex = mergeRowIndex;
}
@Override
protected void merge(
Sheet sheet,
Cell cell,
Head head,
Integer relativeRowIndex
) {
int rowIndex = cell.getRowIndex();
int colIndex = cell.getColumnIndex();
// 必须这样使用,不然lastRow 永远为空
sheet = cell.getSheet();
Row lastRow = sheet.getRow(rowIndex - 1);
if (lastRow == null) {
log.warn("rowIndex is {}, lastRow is null , data is {} ", rowIndex, cell);
return;
}
Cell lastRowCell = lastRow.getCell(colIndex);
// 先进行列的判断,再进行的判断
if (colIndex != mergeRowIndex) {
return;
}
// log.info("rowIndex is {}, data is {}", rowIndex, cell.toString());
// 如果数据相同,才进行合并
if (!lastRowCell.getStringCellValue().equals(cell.getStringCellValue())) {
return;
}
// 检查当前单元格是否已在合并中,如果在,则修改合数据;如果不存在,新建
List<CellRangeAddress> list = sheet.getMergedRegions();
for (int i = 0, listSize = list.size(); i < listSize; i++) {
CellRangeAddress cellAddresses = list.get(i);
// 检查列是否相同
if (cellAddresses.getFirstColumn() != colIndex || cellAddresses.getLastColumn() != colIndex) {
continue;
}
// 因为数据是从上到下添加的,行数是依次递增的,所以只需要检查合并的最后一行和上一行是否相同即可。
if (cellAddresses.getLastRow() == lastRowCell.getRowIndex()) {
// 见最后一行,改为当前行
cellAddresses.setLastRow(rowIndex);
sheet.removeMergedRegion(i);
sheet.addMergedRegion(cellAddresses);
return;
}
}
// 不存在已合并的区域,新建合格合并区域
CellRangeAddress cra = new CellRangeAddress(lastRowCell.getRowIndex(), cell.getRowIndex(), lastRowCell.getColumnIndex(), cell.getColumnIndex());
sheet.addMergedRegion(cra);
}
}
使用
try (ExcelWriter excelWriter = EasyExcel.write(filePath).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("对账单")
.registerWriteHandler(new ColumnSameDataMergeStrategy(0))
.build();
Map<String, Object> map = MapUtils.newHashMap();
map.put("title", title);
map.put("time", reconciliationCycle);
excelWriter.fill(map, writeSheet);
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(byStoreDtoList, fillConfig, writeSheet);
// excelWriter.fill(byStoreDtoList, writeSheet);
}
注意,在使用时,依赖的是框架提供的数据,需要设置 forceNewRow
为 true
,将所有数据放入内存中,一次保存。否则会出现问题。