EasyExcel动态单元格合并(跨行或跨列)

EasyExcel动态单元格合并(跨行或跨列)

  • 简单的合并单元格可以参照官网提供的@OnceAbsoluteMerge()和@ContentLoopMerge()两个注解进行

@OnceAbsoluteMerge()注解只会合并一次就不再执行了

动态相同值合并单元格代码示例(可以直接使用):

先看结果:

  1. 开启合并列行合并单元格,指定1,3,5,6列进行跨行合并结果图:

    跨行跨列

  2. 开启行合并结果图(指定1,3,5,6列进行跨行合并)

    跨行

  3. 开启列合并结果图

    跨列

具体代码显示:

  1. 测试使用(先创建两个类麻烦,测试类中将注释放开可以使用内部类进行测试)

    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.metadata.Head;
    import com.alibaba.excel.write.merge.AbstractMergeStrategy;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.util.CellRangeAddress;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import java.io.File;
    import java.util.*;
    /**
    * 功能描述:easyExcel单元格导出合并测试类,
    *
    * @author SXT
    * @version 1.0
    * @date 2024/3/8 20:56
    */
    public class EasyExcelTest {
    /** excel表头 */
    private List<List<String>> head;
    /** excel内容 */
    private List<List<Object>> data;
    @Test
    public void testMergeCell(){
    File file = new File("C:\\Users\\sxtvv\\Desktop\\testExcel合并.xlsx");
    EasyExcel.write(file)
    .head(head)
    .sheet()
    // 开启相等值 列合并,行合并,从行索引1(excel中第二行)开始合并, 指定跨行合并的列索引
    //.registerWriteHandler(new MergeCellStrategyHandler(true, true, 1, Set.of(0, 2, 4, 5)))
    // 开启相等值 列合并,从行索引1(excel中第二行)开始合并
    //.registerWriteHandler(new MergeCellStrategyHandler(true, 1))
    // 开启相等值 行合并,从行索引1(excel中第二行)开始合并, 指定跨行合并的列索引
    .registerWriteHandler(new MergeCellStrategyHandler(true, 1, Set.of(0, 2, 4, 5)))
    .doWrite(data);
    System.out.println("导出成功");
    }
    @BeforeEach
    public void buildHead(){
    List<List<String>> head = new ArrayList<>();
    head.add(List.of("字段1"));
    head.add(List.of("字段2"));
    head.add(List.of("字段3"));
    head.add(List.of("字段4"));
    head.add(List.of("字段5"));
    head.add(List.of("字段6"));
    this.head = head;
    }
    @BeforeEach
    public void buildData(){
    List<List<Object>> data = new ArrayList<>();
    Set<Integer> column = Set.of(2, 4, 5);
    Set<Integer> row = Set.of(2,3, 6,7,8);
    // 重复
    int repeat = 2;
    for (int i = 0; i < 10; i++) {
    List<Object> el = new ArrayList<>(6);
    if (row.contains(i)) {
    el.add("相同");
    el.add("测试-1");
    el.add("相同");
    el.add("测试-" + i);
    el.add("测试-" + i);
    el.add("测试-" + i);
    }else {
    for (int j = 0; j <6; j++) {
    if (column.contains(j)) {
    el.add("相同");
    }else {
    el.add("测试" + (i + 1) + "-" + (j + 1));
    }
    }
    }
    data.add(el);
    }
    this.data = data;
    }
    /**
    * 规则: 优先合并列,再合并行
    */
    //static class MergeCellStrategyHandler extends AbstractMergeStrategy {
    // /**
    // * 相同列合并
    // */
    // private boolean alikeColumn;
    //
    // /**
    // * 相同行合并
    // */
    // private boolean alikeRow;
    //
    // /** 开始进行合并的行index */
    // private int rowIndex;
    //
    // /** 跨行合并的列index */
    // private Set<Integer> columns;
    //
    // private int currentRowIndex = 0;
    //
    // public MergeCellStrategyHandler(boolean alikeColumn, boolean alikeRow, int rowIndex, Set<Integer> columns){
    // Assertions.assertNotNull(columns, "指定列跨行合并不能为空");
    // this.alikeColumn = alikeColumn;
    // this.alikeRow = alikeRow;
    // this.rowIndex = rowIndex;
    // this.columns = columns;
    // }
    //
    // /**
    // * 指定是否进行跨列合并单元格
    // * @param alikeColumn
    // * @param rowIndex
    // */
    // public MergeCellStrategyHandler(boolean alikeColumn, int rowIndex){
    // this(alikeColumn, false, rowIndex, new HashSet<>());
    // }
    //
    // /**
    // * 指定是否进行跨行合并单元格
    // * @param alikeRow
    // * @param rowIndex
    // * @param columns
    // */
    // public MergeCellStrategyHandler(boolean alikeRow, int rowIndex, Set<Integer> columns){
    // this(false, alikeRow, rowIndex, columns);
    // }
    //
    //
    // @Override
    // protected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {
    // int rowId = cell.getRowIndex();
    // currentRowIndex = rowId == currentRowIndex ? currentRowIndex : rowId;
    // if (rowIndex > rowId) {
    // return;
    // }
    // int columnId = cell.getColumnIndex();
    // // 列合并
    // if (alikeColumn && columnId > 0) {
    // String currentCellVal = this.getCellVal(cell);
    // Cell preCell = cell.getRow().getCell(columnId - 1);
    // String preCellVal = this.getCellVal(preCell);
    // if (null != currentCellVal && null != preCellVal && !preCellVal.isBlank() && !currentCellVal.isBlank()) {
    // // 当前单元格内容与上一个单元格内容相等,进行合并处理
    // if (preCellVal.equals(currentCellVal)) {
    // CellRangeAddress rangeAddress = new CellRangeAddress(currentRowIndex, currentRowIndex, columnId - 1, columnId);
    // rangeAddress = this.findExistAddress(sheet, rangeAddress, currentCellVal);
    // if (null != rangeAddress) {
    // sheet.addMergedRegion(rangeAddress);
    // }
    // }
    // }
    // }
    // // 行合并
    // if (alikeRow && rowIndex < rowId && columns.contains(columnId)) {
    // String currentCellVal = this.getCellVal(cell);
    // Cell preCell = sheet.getRow(rowId - 1).getCell(columnId);
    // String preCellVal = this.getCellVal(preCell);
    // if (null != currentCellVal && null != preCellVal && !preCellVal.isBlank() && !currentCellVal.isBlank()) {
    // // 当前单元格内容与上一行单元格内容相等,进行合并处理
    // if (preCellVal.equals(currentCellVal)) {
    // //sheet.validateMergedRegions();
    // CellRangeAddress rangeAddress = new CellRangeAddress(currentRowIndex - 1, currentRowIndex, columnId, columnId);
    // rangeAddress = this.findExistAddress(sheet, rangeAddress, currentCellVal);
    // if (null != rangeAddress) {
    // sheet.addMergedRegion(rangeAddress);
    // }
    // }
    // }
    // }
    //
    // }
    //
    // /**
    // * 合并单元格地址范围,发现存在相同的地址则进行扩容合并
    // *
    // * @param isColumn true:跨列合并 false:跨行合并
    // * @param sheet
    // * @param rangeAddress :
    // * @param currentVal 当前单元格中的值
    // * @return
    // */
    // private CellRangeAddress findExistAddress(Sheet sheet, CellRangeAddress rangeAddress, String currentVal) {
    // List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
    // int existIndex = 0;
    // Map<Integer, CellRangeAddress> existIdexMap = new LinkedHashMap<>();
    // if (null != mergedRegions && !mergedRegions.isEmpty()) {
    // //验证当前合并的单元格是否存在重复(fixme 重复问题待处理!!!)
    // for (CellRangeAddress mergedRegion : mergedRegions) {
    // if (mergedRegion.intersects(rangeAddress)) {
    // existIdexMap.put(existIndex, mergedRegion);
    // }
    // existIndex++;
    // }
    // }
    // if (existIdexMap.isEmpty()) {
    // return rangeAddress;
    // }
    // List<Integer> existIndexList = new ArrayList<>(existIdexMap.size());
    // for (Map.Entry<Integer, CellRangeAddress> addressEntry : existIdexMap.entrySet()) {
    // CellRangeAddress exist = addressEntry.getValue();
    // // 自动进行单元格合并处理
    // int firstRow = rangeAddress.getFirstRow();
    // int lastRow = rangeAddress.getLastRow();
    // int firstColumn = rangeAddress.getFirstColumn();
    // int lastColumn = rangeAddress.getLastColumn();
    //
    // int firstRow1 = exist.getFirstRow();
    // int lastRow1 = exist.getLastRow();
    // int firstColumn1 = exist.getFirstColumn();
    // int lastColumn1 = exist.getLastColumn();
    // // 跨行合并 最后一列相等, 行不相等
    // if (lastRow > lastRow1 && lastColumn == lastColumn1) {
    // // 检查进行跨行合并的单元格是否已经存在跨列合并
    // if (lastColumn > 0 && firstColumn1 != lastColumn1) {
    // // 获取当前单元格的前一列单元格
    // String cellVal = this.getCellVal(sheet.getRow(lastRow).getCell(lastColumn - 1));
    // if (null != cellVal && cellVal.equals(currentVal)) {
    // exist.setLastRow(lastRow);
    // }
    // } else {
    // exist.setLastRow(lastRow);
    // }
    // rangeAddress = exist;
    // existIndexList.add(addressEntry.getKey());
    // }
    //
    // // 跨列合并 行相等,列不相等
    // if (lastColumn > lastColumn1 && firstRow == firstRow1 ) {
    // exist.setLastColumn(lastColumn);
    // rangeAddress = exist;
    // existIndexList.add(addressEntry.getKey());
    // }
    // }
    // // 移除已经存在且冲突的合并数据
    // if (existIndexList.isEmpty()) {
    // rangeAddress = null;
    // }else {
    // sheet.removeMergedRegions(existIdexMap.keySet());
    // }
    // return rangeAddress;
    // }
    //
    // private String getCellVal(Cell cell) {
    // String val = null;
    // try {
    // val = cell.getStringCellValue();
    // }catch (Exception e){
    // System.out.printf("读取单元格内容失败:行%d 列%d %n", (cell.getRowIndex() + 1), (cell.getColumnIndex() + 1));
    // }
    // return val;
    // }
    //}
    }
  2. 使用easyexce导出excel并进合并单元格可以直接实现CellWriteHandler接口进行单元格合并,也可以继承AbstractMergeStrategy类实现抽象方法merge()进行,下列代码使用继承的方式显示单元格合并(核心类)

    import com.alibaba.excel.metadata.Head;
    import com.alibaba.excel.write.merge.AbstractMergeStrategy;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.util.CellRangeAddress;
    import java.util.*;
    /**
    * 功能描述:规则: 优先合并列,再合并行
    *
    * @author SXT
    * @version 1.0
    * @date 2024/3/9 15:12
    */
    public class MergeCellStrategyHandler extends AbstractMergeStrategy {
    /**
    * 相同列合并
    */
    private boolean alikeColumn;
    /**
    * 相同行合并
    */
    private boolean alikeRow;
    /** 开始进行合并的行index */
    private int rowIndex;
    /** 跨行合并的列index */
    private Set<Integer> columns;
    private int currentRowIndex = 0;
    /**
    * 构造方法,指定合并方式
    * @param alikeColumn
    * @param alikeRow
    * @param rowIndex
    * @param columns
    */
    public MergeCellStrategyHandler(boolean alikeColumn, boolean alikeRow, int rowIndex, Set<Integer> columns){
    this.alikeColumn = alikeColumn;
    this.alikeRow = alikeRow;
    this.rowIndex = rowIndex;
    this.columns = columns;
    }
    /**
    * 指定是否进行跨列合并单元格
    * @param alikeColumn
    * @param rowIndex
    */
    public MergeCellStrategyHandler(boolean alikeColumn, int rowIndex){
    this(alikeColumn, false, rowIndex, new HashSet<>());
    }
    /**
    * 指定是否进行跨行合并单元格
    * @param alikeRow
    * @param rowIndex
    * @param columns
    */
    public MergeCellStrategyHandler(boolean alikeRow, int rowIndex, Set<Integer> columns){
    this(false, alikeRow, rowIndex, columns);
    }
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {
    int rowId = cell.getRowIndex();
    currentRowIndex = rowId == currentRowIndex ? currentRowIndex : rowId;
    if (rowIndex > rowId) {
    return;
    }
    int columnId = cell.getColumnIndex();
    // 列合并
    if (alikeColumn && columnId > 0) {
    String currentCellVal = this.getCellVal(cell);
    Cell preCell = cell.getRow().getCell(columnId - 1);
    String preCellVal = this.getCellVal(preCell);
    if (null != currentCellVal && null != preCellVal && !preCellVal.isBlank() && !currentCellVal.isBlank()) {
    // 当前单元格内容与上一个单元格内容相等,进行合并处理
    if (preCellVal.equals(currentCellVal)) {
    CellRangeAddress rangeAddress = new CellRangeAddress(currentRowIndex, currentRowIndex, columnId - 1, columnId);
    rangeAddress = this.findExistAddress(sheet, rangeAddress, currentCellVal);
    if (null != rangeAddress) {
    sheet.addMergedRegion(rangeAddress);
    }
    }
    }
    }
    // 行合并
    if (alikeRow && rowIndex < rowId && columns.contains(columnId)) {
    String currentCellVal = this.getCellVal(cell);
    Cell preCell = sheet.getRow(rowId - 1).getCell(columnId);
    String preCellVal = this.getCellVal(preCell);
    if (null != currentCellVal && null != preCellVal && !preCellVal.isBlank() && !currentCellVal.isBlank()) {
    // 当前单元格内容与上一行单元格内容相等,进行合并处理
    if (preCellVal.equals(currentCellVal)) {
    //sheet.validateMergedRegions();
    CellRangeAddress rangeAddress = new CellRangeAddress(currentRowIndex - 1, currentRowIndex, columnId, columnId);
    rangeAddress = this.findExistAddress(sheet, rangeAddress, currentCellVal);
    if (null != rangeAddress) {
    sheet.addMergedRegion(rangeAddress);
    }
    }
    }
    }
    }
    /**
    * 合并单元格地址范围,发现存在相同的地址则进行扩容合并
    *
    * @param sheet
    * @param rangeAddress 单元格合并地址
    * @param currentVal 当前单元格中的值
    * @return
    */
    private CellRangeAddress findExistAddress(Sheet sheet, CellRangeAddress rangeAddress, String currentVal) {
    List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
    int existIndex = 0;
    Map<Integer, CellRangeAddress> existIdexMap = new LinkedHashMap<>();
    if (null != mergedRegions && !mergedRegions.isEmpty()) {
    //验证当前合并的单元格是否存在重复
    for (CellRangeAddress mergedRegion : mergedRegions) {
    if (mergedRegion.intersects(rangeAddress)) {
    existIdexMap.put(existIndex, mergedRegion);
    }
    existIndex++;
    }
    }
    if (existIdexMap.isEmpty()) {
    return rangeAddress;
    }
    List<Integer> existIndexList = new ArrayList<>(existIdexMap.size());
    for (Map.Entry<Integer, CellRangeAddress> addressEntry : existIdexMap.entrySet()) {
    CellRangeAddress exist = addressEntry.getValue();
    // 自动进行单元格合并处理
    int firstRow = rangeAddress.getFirstRow();
    int lastRow = rangeAddress.getLastRow();
    int firstColumn = rangeAddress.getFirstColumn();
    int lastColumn = rangeAddress.getLastColumn();
    int firstRow1 = exist.getFirstRow();
    int lastRow1 = exist.getLastRow();
    int firstColumn1 = exist.getFirstColumn();
    int lastColumn1 = exist.getLastColumn();
    // 跨行合并 最后一列相等, 行不相等
    if (lastRow > lastRow1 && lastColumn == lastColumn1) {
    // 检查进行跨行合并的单元格是否已经存在跨列合并
    if (lastColumn > 0 && firstColumn1 != lastColumn1) {
    // 获取当前单元格的前一列单元格
    String cellVal = this.getCellVal(sheet.getRow(lastRow).getCell(lastColumn - 1));
    if (null != cellVal && cellVal.equals(currentVal)) {
    exist.setLastRow(lastRow);
    }
    } else {
    exist.setLastRow(lastRow);
    }
    rangeAddress = exist;
    existIndexList.add(addressEntry.getKey());
    }
    // 跨列合并 行相等,列不相等
    if (lastColumn > lastColumn1 && firstRow == firstRow1 ) {
    exist.setLastColumn(lastColumn);
    rangeAddress = exist;
    existIndexList.add(addressEntry.getKey());
    }
    }
    // 移除已经存在且冲突的合并数据
    if (existIndexList.isEmpty()) {
    rangeAddress = null;
    }else {
    sheet.removeMergedRegions(existIdexMap.keySet());
    }
    return rangeAddress;
    }
    /**
    * 获取单元格中的内容
    * @param cell
    * @return
    */
    private String getCellVal(Cell cell) {
    String val = null;
    try {
    val = cell.getStringCellValue();
    }catch (Exception e){
    System.out.printf("读取单元格内容失败:行%d 列%d %n", (cell.getRowIndex() + 1), (cell.getColumnIndex() + 1));
    }
    return val;
    }
    }
posted @   酸菜鱼没有鱼  阅读(10346)  评论(7编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
历史上的今天:
2023-03-09 ant-design-vue 视频上传组件封装处理
点击右上角即可分享
微信分享提示