easyExcel或poi导出excel合并单元格的问题(java)

原文链接:EasyExcel导出多列相同合并处理以及单列相同合并处理_easyexcel怎么实现多个列分别合并-CSDN博客
我对自定义多列相同合并策略处理类进行了一些修改,原文进行多行合并的时候会导致报错(上一行已合并的问题),所以处理了一下重新添加合并单元的逻辑,代码如下:
 

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 java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

/**
 * @ClassName:ExcelMergeCustomerCellHandler @Description:easyexcel合并策略处理类,多列相同合并策略 @Version:1.0 @Date:2023/9/7
 * 09:36:52
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExcelCellByMultiHandler implements CellWriteHandler {

  /** 用第几行开始合并 ,默认为1,因为第0行是标题,EasyExcel 的默认也是 */
  private int mergeRowIndex = 1;

  /** 参与合并的列,起始列位置,默认为0 */
  private int mergeColumnStart = 0;

  /** 参与合并的列,最后一列的位置,默认为4 */
  private int mergeColumnEnd = 4;

  /*
   * 在对单元格的所有操作完成后调用
   */
  @Override
  public void afterCellDispose(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      List<WriteCellData<?>> list,
      Cell cell,
      Head head,
      Integer integer,
      Boolean aBoolean) {
    // 当前行
    int curRowIndex = cell.getRowIndex();
    // 当前列
    int curColIndex = cell.getColumnIndex();
    if (curRowIndex > mergeRowIndex && curColIndex >= mergeColumnStart) {
      // 读取到mergeColumnEnd ,开始调用合并方法
      if (curColIndex == mergeColumnEnd) {
        mergeWithPrevRow(writeSheetHolder, cell, curRowIndex);
      }
    }
  }

  /**
   * @Description: 合并比较多列值是否相同 @Date: 2023/9/8 16:31 @Param writeSheetHolder: 通过此对象获取sheet等信息 @Param
   * cell: 当前单元格 @Param curRowIndex: 当前行
   *
   * @return: void
   */
  private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex) {
    StringBuilder curString = new StringBuilder();
    StringBuilder preString = new StringBuilder();
    for (int i = mergeColumnStart; i <= mergeColumnEnd; i++) {
      Cell curCell = cell.getSheet().getRow(curRowIndex).getCell(i);
      Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(i);
      // 根据单元格类型获取单元格的值
      Object curData =
          curCell.getCellType() == CellType.STRING
              ? curCell.getStringCellValue()
              : curCell.getNumericCellValue();
      Object preData =
          preCell.getCellType() == CellType.STRING
              ? preCell.getStringCellValue()
              : preCell.getNumericCellValue();
      curString.append(curData);
      preString.append(preData);
    }
    // 比较当前行的单元格与上一行是否相同,相同合并当前单元格与上一行
    if (curString.toString().equals(preString.toString())) {
      Sheet sheet = writeSheetHolder.getSheet();
      // 获取合并信息
      boolean isMerged = false;
      // 下标
      for (int j = 0; j <= mergeColumnEnd; j++) {
        List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
        for (int i = 0; i < mergeRegions.size(); i++) {
          CellRangeAddress cellRangeAddr = mergeRegions.get(i);
          // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
          if (cellRangeAddr.isInRange(curRowIndex - 1, j)) {
            sheet.removeMergedRegion(i);
            cellRangeAddr.setLastRow(curRowIndex);
            sheet.addMergedRegion(cellRangeAddr);
            isMerged = true;
            break;
          }
        }
      }
      if (!isMerged) {
        for (int i = 0; i <= mergeColumnEnd; i++) {
          CellRangeAddress cellRangeAddress =
              new CellRangeAddress(curRowIndex - 1, curRowIndex, i, i);
          sheet.addMergedRegion(cellRangeAddress);
        }
      }
    }
  }
}

上面的三个参数根据实际导出情况自行调整

### 使用 EasyExcel 根据模板导出数据并合并单元 在处理 Excel 数据导出时,EasyExcel 提供了一种简便的方式来根据模板导出数据,并能够灵活地合并单元。这不仅提高了开发效率,也使得生成的报表更加美观易读。 #### 准备工作 为了实现基于模板的数据导出和单元合并功能,首先需要引入 EasyExcel 的依赖库: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>最新版本号</version> </dependency> ``` #### 创建实体类 假设有一个简单的员工信息表作为例子,其中包含姓名、部门以及入职日期三个字段,则对应的 Java 实体类如下所示: ```java public class EmployeeData { @ExcelProperty("姓名") private String name; @ExcelProperty("部门") private String department; @ExcelProperty("入职日期") private Date joinDate; // Getters and Setters... } ``` #### 定义模板文件 创建一个名为 `template.xlsx` 的 Excel 文件,在此文件内设计好所需的布局样式,包括但不限于字体大小、颜色等视觉属性设置。特别需要注意的是要预留足够的空白区域用于后续动态填充实际业务数据[^2]。 #### 编写导出逻辑 编写一段程序代码用来加载上述定义好的模板文件,并向其内部填入具体数值的同时完成特定条件下的多行多列之间的自动合并不重复显示相同内容的操作: ```java import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.handler.WriteHandler; import org.apache.poi.ss.usermodel.*; import java.util.List; // 自定义处理器以实现按需合并单元的功能 class CustomMergeStrategy implements WriteHandler { @Override public void sheet(int sheetNo, Sheet sheet) {} @Override public void row(int rowNum, Row row) {} /** * 当前方法会在每次写入一行记录之前被调用, * 可在此处判断是否应该对该行中的某些列执行跨行/列操作。 */ @Override public void cell(int cellNum, Cell cell) { // 示例:如果当前是第二列(即索引为1),则尝试向上寻找连续相等的内容进行纵向合并 if (cell.getColumnIndex() == 1 && !isFirstRow(cell.getRow())) { mergeCellsIfEqual(sheet, cell); } } private boolean isFirstRow(Row row){ return row.getRowNum()==0; } private void mergeCellsIfEqual(Sheet sheet, Cell currentCell) { int columnIndex = currentCell.getColumnIndex(); int rowIndex = currentCell.getRowIndex(); while (--rowIndex >= 0) { Row previousRow = sheet.getRow(rowIndex); if (previousRow != null) { Cell prevCell = previousRow.getCell(columnIndex); if ((prevCell != null) && (currentCell.getStringCellValue().equals(prevCell.getStringCellValue()))) { Region regionToBeMerged = new Region(currentCell.getRowIndex(), (short)columnIndex, previousRow.getLastCellNum()-1,(short)columnIndex); sheet.addMergedRegion(regionToBeMerged); break; } else { break; } } } } } // 主函数部分负责启动整个流程 List<EmployeeData> employeeDatas = ... ; // 获取待导出的数据集 String templatePath = "path/to/template.xlsx"; String outputPath = "output/path/result.xlsx"; EasyExcel.write(outputPath) .withTemplate(templatePath) .registerWriteHandler(new CustomMergeStrategy()) .sheet() .doFill(employeeDatas); ``` 这段代码展示了如何通过继承 `WriteHandler` 接口来自定义处理机制,从而实现在满足一定条件下对相邻单元实施横向竖直方向上的合并动作。同时利用了 Apache POI 库所提供的 API 来辅助完成具体的区域设定任务[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值