Skip to content

feature: 指定某一列,当相邻单元格内容一致时合并处理 #256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package cn.idev.excel.write.merge;

import cn.idev.excel.write.handler.RowWriteHandler;
import cn.idev.excel.write.handler.context.RowWriteHandlerContext;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.ArrayDeque;
import java.util.Deque;

/**
* @Description Specifies that the column merges adjacent cells with the same content
* @Date 2025/3/8
*/
public class DynamicMergeStrategy implements RowWriteHandler {

/**
* You want to merge columns that are adjacent to the same cell data
*/
private final int columnIndex;
/**
* Extend column
*/
private final int columnExtend;
/**
* size of collection date
*/
private final int dataSize;
private final Deque<MergeRow> rowStack = new ArrayDeque<>();

public DynamicMergeStrategy(int columnIndex,int dataSize) {
this(columnIndex,1,dataSize);
}
public DynamicMergeStrategy(int columnIndex, int columnExtend,int dataSize) {
if (columnExtend < 1) {
throw new IllegalArgumentException("ColumnExtend must be greater than 1");
}
if (columnIndex < 0) {
throw new IllegalArgumentException("ColumnIndex must be greater than 0");
}
if(dataSize<=0){
throw new IllegalArgumentException("dataSize must be greater than 0");
}
this.columnIndex = columnIndex;
this.columnExtend = columnExtend;
this.dataSize = dataSize;

}
@Override
public void afterRowDispose(RowWriteHandlerContext context) {
if (context.getHead() || context.getRelativeRowIndex() == null) {
return;
}
Row row = context.getRow();
rowStack.push(new MergeRow(row, context.getRelativeRowIndex()));
if(context.getRelativeRowIndex()==(dataSize-1)){
while (!rowStack.isEmpty()){
MergeRow lastRow = rowStack.pop();
while (!rowStack.isEmpty()){
MergeRow prevRow = rowStack.pop();

if (!prevRow.getRow().getCell(columnIndex).getStringCellValue().equals(lastRow.getRow().getCell(columnIndex).getStringCellValue())) {
if(lastRow.getRow().getRowNum()!=(prevRow.getRow().getRowNum()+1)){
CellRangeAddress cellRangeAddress = new CellRangeAddress(prevRow.getRow().getRowNum()+1,
lastRow.getRow().getRowNum(), columnIndex, columnIndex + columnExtend - 1);
context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress);
}
rowStack.push(prevRow);
break;
}else {
if(prevRow.getRelativeRowIndex().equals(0)){
CellRangeAddress cellRangeAddress = new CellRangeAddress(prevRow.getRow().getRowNum(),
lastRow.getRow().getRowNum(), columnIndex, columnIndex + columnExtend - 1);
context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress);
}
}
}

}
}

}

@Data
@AllArgsConstructor
public static class MergeRow{
private Row row;
private Integer relativeRowIndex;
}
}
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;

import cn.idev.excel.EasyExcel;
import cn.idev.excel.ExcelWriter;
@@ -18,6 +20,7 @@
import cn.idev.excel.util.ListUtils;
import cn.idev.excel.write.handler.CellWriteHandler;
import cn.idev.excel.write.handler.context.CellWriteHandlerContext;
import cn.idev.excel.write.merge.DynamicMergeStrategy;
import cn.idev.excel.write.merge.LoopMergeStrategy;
import cn.idev.excel.write.style.HorizontalCellStyleStrategy;
import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
@@ -613,6 +616,148 @@ public void dynamicHeadWrite() {
.doWrite(data());
}

/**
* 动态头,实时生成头写入
* 指定列值相同时合并
*/
@Test
public void customHeadReadAndDynamicMergeStrategy() {
List<Map<Integer, String>> maps = yearData();
EasyExcel.write(TestFileUtil.getPath() + "customHeadRead" + System.currentTimeMillis() + ".xlsx")
.head(yearHead())
.sheet("模板")
.registerWriteHandler(new DynamicMergeStrategy(0,maps.size()))
.registerWriteHandler(new DynamicMergeStrategy(2,maps.size()))
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(maps);

List<Map<Integer, String>> maps1 = yearData1();
EasyExcel.write(TestFileUtil.getPath() + "customHeadRead" + System.currentTimeMillis() + ".xlsx")
.head(yearHead())
.sheet("模板")
.registerWriteHandler(new DynamicMergeStrategy(0,maps1.size()))
.registerWriteHandler(new DynamicMergeStrategy(2,maps1.size()))
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(maps1);
List<Map<Integer, String>> maps2 = yearData2();
EasyExcel.write(TestFileUtil.getPath() + "customHeadRead" + System.currentTimeMillis() + ".xlsx")
.head(yearHead())
.sheet("模板")
.registerWriteHandler(new DynamicMergeStrategy(0,maps2.size()))
.registerWriteHandler(new DynamicMergeStrategy(2,maps2.size()))
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(maps2);
List<Map<Integer, String>> maps3 = yearData3();
EasyExcel.write(TestFileUtil.getPath() + "customHeadRead" + System.currentTimeMillis() + ".xlsx")
.head(yearHead())
.sheet("模板")
.registerWriteHandler(new DynamicMergeStrategy(0,maps3.size()))
.registerWriteHandler(new DynamicMergeStrategy(2,maps3.size()))
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(maps3);

List<Map<Integer, String>> maps4 = yearData4();
EasyExcel.write(TestFileUtil.getPath() + "customHeadRead" + System.currentTimeMillis() + ".xlsx")
.head(yearHead())
.sheet("模板")
.registerWriteHandler(new DynamicMergeStrategy(0,maps4.size()))
.registerWriteHandler(new DynamicMergeStrategy(2,maps4.size()))
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(maps4);
}

public List<List<String>> yearHead() {
List<List<String>> head = new ArrayList<>();
for (int i = 0; i < 12; i++) {
List<String> h = new ArrayList<>();
if(i<3){
h.add("第一季度");
}
if(i>=3&&i<6){
h.add("第二季度");
}
if(i>=6&&i<9){
h.add("第三季度");
}
if(i>=9){
h.add("第四季度");
}
h.add("第" + (i + 1) + "月");
head.add(h);
}
return head;
}
public List<Map<Integer, String>> yearData() {
List<Map<Integer, String>> data = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Map<Integer, String> map = new HashMap<>();
for (int j = 0; j < 12; j++) {
if(i<20){
map.put( j , "第" + (j + 1) + "月"+"前20条数据");
}else {
map.put( j , "第" + (j + 1) + "月");
}

}
data.add(map);
}
return data;
}
public List<Map<Integer, String>> yearData1() {
List<Map<Integer, String>> data = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Map<Integer, String> map = new HashMap<>();
for (int j = 0; j < 12; j++) {
if(i>0){
map.put( j , (j + 1) + "");
}else {
map.put(j,i+"");
}

}
data.add(map);
}
return data;
}
public List<Map<Integer, String>> yearData4() {
List<Map<Integer, String>> data = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Map<Integer, String> map = new HashMap<>();
for (int j = 0; j < 12; j++) {
if(i<2){
map.put( j , (j + 1) + "");
}else {
map.put(j,i+"");
}

}
data.add(map);
}
return data;
}
public List<Map<Integer, String>> yearData3() {
List<Map<Integer, String>> data = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Map<Integer, String> map = new HashMap<>();
for (int j = 0; j < 12; j++) {
map.put(j,j+"");

}
data.add(map);
}
return data;
}
public List<Map<Integer, String>> yearData2() {
List<Map<Integer, String>> data = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Map<Integer, String> map = new HashMap<>();
for (int j = 0; j < 12; j++) {
map.put(j,i+"");
}
data.add(map);
}
return data;
}
/**
* 自动列宽(不太精确)
* <p>