需求:同一列上下行内容相同的情况下进行合并,有一个前提要求是某一列的值相同情况下,再去判断要合并的列是否值相同。
比如下面的核心代码中前提是第19列的值相同的前提下,再去判断要合并的上下行是否相同,如果相同就合并,不相同就不合并。这里有个坑,可以继续往下看。
实现:
java
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.Data;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
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;
import java.util.Objects;
@Data
public class ExcelFillCellMergeStrategy implements CellWriteHandler {
//需要合并的列
private int[] mergeColumnIndex;
//需要从第二行开始,列头第二行
private int mergeRowIndex;
public ExcelFillCellMergeStrategy() {
}
public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
this.mergeRowIndex = mergeRowIndex;
this.mergeColumnIndex = mergeColumnIndex;
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<WriteCellData<?>> cellDataList,
Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
//当前行
int curRowIndex = cell.getRowIndex();
//当前列
int curColIndex = cell.getColumnIndex();
if (curRowIndex > mergeRowIndex) {
for (int i = 0; i < mergeColumnIndex.length; i++) {
if (curColIndex == mergeColumnIndex[i] && curColIndex == 19) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
} else if (curColIndex == mergeColumnIndex[i] && curColIndex != 19) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
}
}
}
}
/**
* 当前单元格向上合并
*
* @param writeSheetHolder
* @param cell 当前单元格
* @param curRowIndex 当前行
* @param curColIndex 当前列
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell,
int curRowIndex, int curColIndex) {
//获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
//当前行第19列的值
Cell businessCodeCell = cell.getSheet().getRow(curRowIndex).getCell(19);
//上一行第19列的值
Cell prebusinessCodeCell = cell.getSheet().getRow(curRowIndex - 1).getCell(19);
String businessCodeData = businessCodeCell.getStringCellValue();
String preBusinessCodeData = prebusinessCodeCell.getStringCellValue();
// 我是因为有一些数据是空串,不进行合并,所以做了特殊处理的。可以根据自己的需求进行修改
// 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
if (Objects.equals(cell.getCellType(), CellType.STRING) && Objects.equals(preCell.getCellType(), CellType.STRING)
&& curData.equals(preData)) {
if (curColIndex != 19 && Objects.equals(businessCodeData, preBusinessCodeData)) {
addmergeRegion(writeSheetHolder, curRowIndex, curColIndex);
} else if (curColIndex == 19) {
addmergeRegion(writeSheetHolder, curRowIndex, curColIndex);
}
} else if (Objects.equals(cell.getCellType(), CellType.NUMERIC) && Objects.equals(preCell.getCellType(), CellType.NUMERIC)
&& curData.equals(preData)
&& Objects.nonNull(businessCodeData) && Objects.nonNull(preBusinessCodeData)
&& Objects.equals(businessCodeData, preBusinessCodeData)) {
addmergeRegion(writeSheetHolder, curRowIndex, curColIndex);
}
}
private void addmergeRegion(WriteSheetHolder writeSheetHolder, int curRowIndex, int curColIndex) {
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);
}
}
}
java
public void export(HttpServletResponse response, InvestmentAndIncomeInfoDTO investmentAndIncomeInfoDTO) throws Exception {
//需要合并的列
int[] mergeColumeIndex = {19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40};
//需要从第二行开始,列头第二行
int mergeRowIndex = 2;
String date = DateTimeUtil.date();
List<DTO> exportData = getExportData();
String fileName = URLEncoder.encode("测试合并" + date + ".xlsx", "UTF-8");
ExcelDataUtil.setResponseHeader(response, fileName);
EasyExcel.write(response.getOutputStream(), InvestmentAndIncomeInfoDTO.class)
.excelType(ExcelTypeEnum.XLSX)
//内容居中
.registerWriteHandler(horizontalCellStyleStrategyBuilder())
.registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex))
.sheet("测试合并")
.doWrite((exportData));
}
让合并的内容居中并且加上框线显示的代码。
java
public HorizontalCellStyleStrategy horizontalCellStyleStrategyBuilder() {
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 单元格边框类型
headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
headWriteCellStyle.setBorderRight(BorderStyle.THIN);
headWriteCellStyle.setBorderTop(BorderStyle.THIN);
// 单元格边框颜色
headWriteCellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
headWriteCellStyle.setRightBorderColor(IndexedColors.BLACK.index);
headWriteCellStyle.setTopBorderColor(IndexedColors.BLACK.index);
headWriteCellStyle.setBottomBorderColor(IndexedColors.BLACK.index);
//内容策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//设置 水平居中
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
//垂直居中
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 单元格边框类型
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
// 单元格边框颜色
contentWriteCellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
contentWriteCellStyle.setRightBorderColor(IndexedColors.BLACK.index);
contentWriteCellStyle.setTopBorderColor(IndexedColors.BLACK.index);
contentWriteCellStyle.setBottomBorderColor(IndexedColors.BLACK.index);
return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
}
上述代码自己可以添加一些样例数据。进行测试。很完美的实现了。如果数据量多的情况下,执行效率是不高的。有什么好的办法欢迎评论留言,一起学习。
说一下这次的坑,此次的需求是在第19列(第19列的值不会为空)值相同的情况下再进行合并,这个时候我又想合并前18列,于是我修改了要合并的列集合。
java
int[] mergeColumeIndex = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40};
运行,报错,空指针!!!非常好,有问题才有进步嘛。
跟代码看看,发现在获取某行第19列值的情况下获取不到。
一路看下来,发现只有在进行合并前18列的时候才会发生这个问题,有点眉目了哈,在合并前18列时,是获取不到当前合并行合并列后的值的!!!
这应该是easyexcel 的特殊机制吧。原生的poi 我没试。有小伙伴试的话欢迎评论留言。
好了,前面的不合并了。就这样吧!!!