easyExcle单元格合并

自定义单元格合并策略:

/*** 自定义单元格合并策略** @create: 2023-11-15 13:41**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class EasyExcelCustomMergeStrategy implements RowWriteHandler {/*** 总数*/private Integer totalNum;//合并行计数private int count;//要合并的列 从0开始private int[] mergeColumnIndex;//已合并单元数private int mergedTotalCount = 0;public EasyExcelCustomMergeStrategy(int[] mergeColumnIndex) {this.mergeColumnIndex = mergeColumnIndex;}// 每写入一行会执行一次afterRowDispose,存在合并行时进行合并方法mergeSameRow,否则什么也不做@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {//当前行索引int curRowNum = row.getRowNum();if (mergeColumnIndex != null && mergeColumnIndex.length > 0 && !isHead) {//当前行第一列单元格Cell curA1Cell = row.getCell(0);Object curA1Data = curA1Cell.getCellTypeEnum() == CellType.STRING ? curA1Cell.getStringCellValue() : curA1Cell.getNumericCellValue();//上一行第一列单元格Cell preA1Cell = row.getSheet().getRow(curRowNum - 1).getCell(0);Object preA1Data = preA1Cell.getCellTypeEnum() == CellType.STRING ? preA1Cell.getStringCellValue() : preA1Cell.getNumericCellValue();if (curA1Data.equals(preA1Data)) {count++;} else {if (count > 0) {for (int columnIndex : mergeColumnIndex) {mergeSameRow(writeSheetHolder, curRowNum, count, columnIndex);}//for (int i = 0; i < mergeColumnIndex.length; i++) {////}count = 0;}}// 最后一行存在合并时,需要单独调用合并,否则不会执行mergeSameRowif (curRowNum == totalNum && count > 0) {for (int columnIndex : mergeColumnIndex) {mergeSameRow(writeSheetHolder, curRowNum + 1, count, columnIndex);}}}}/*** 按列合并单元格** @param writeSheetHolder 写出处理* @param curRowIndex      当前行索引,有n行固定行就加n* @param needMergeNum     需要合并的行* @param curColIndex      需要合并的列*/private void mergeSameRow(WriteSheetHolder writeSheetHolder, int curRowIndex, int needMergeNum, int curColIndex) {Sheet sheet = writeSheetHolder.getSheet();try {Field sh = sheet.getClass().getDeclaredField("_sh");sh.setAccessible(true);XSSFSheet shSheet = (XSSFSheet) sh.get(sheet);CTWorksheet worksheet = shSheet.getCTWorksheet();CTMergeCells ctMergeCells = mergedTotalCount > 0 ? worksheet.getMergeCells() : worksheet.addNewMergeCells();CTMergeCell ctMergeCell = ctMergeCells.addNewMergeCell();CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - needMergeNum - 1, curRowIndex - 1, curColIndex, curColIndex);ctMergeCell.setRef(cellAddresses.formatAsString());mergedTotalCount++;} catch (Exception e) {log.error("导出出错!", e);throw new BusinessException("导出出错!\n");}}
}

easyExcel提供了两种合并单元格方法:addMergedRegion和addMergedRegionUnsafe,在使用中发现会遇到不同的问题:

如果将需要合并的数据一次性传入afterRowDispose方法进行合并,在合并过程中使用addMergedRegionUnsafe方法合并单元格可能会导致工作簿损坏,而使用addMergedRegion会进行单元格是否重复合并的校验,会直接抛出异常。

使用addMergedRegionUnsafe方法合并单元格导致的问题:

使用addMergedRegion进行的校验:

/*** adds a merged region of cells (hence those cells form one)** @param region (rowfrom/colfrom-rowto/colto) to merge* @param validate whether to validate merged region* @return index of this region* @throws IllegalArgumentException if region contains fewer than 2 cells* @throws IllegalStateException if region intersects with an existing merged region* or multi-cell array formula on this sheet*/
private int addMergedRegion(CellRangeAddress region, boolean validate) {if (region.getNumberOfCells() < 2) {// 区域包含少于 2 个单元格throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");}region.validate(SpreadsheetVersion.EXCEL97);if (validate) {// throw IllegalStateException if the argument CellRangeAddress intersects with// a multi-cell array formula defined in this sheet// 此工作表上的多单元格数组公式validateArrayFormulas(region);// Throw IllegalStateException if the argument CellRangeAddress intersects with// a merged region already in this sheet// 区域与现有合并区域相交validateMergedRegions(region);}return _sheet.addMergedRegion(region.getFirstRow(),region.getFirstColumn(),region.getLastRow(),region.getLastColumn());
}

导致错误产生的代码:

public class EasyExcelCustomMergeStrategy implements RowWriteHandler {//合并坐标集合private List<CellRangeAddress> cellRangeAddress;//从哪行开始private int beginRow;//合并行数private int mergeRows;public PiFillMergeStrategy(List<CellRangeAddress> cellRangeAddress, int beginRow, int mergeRows) {this.cellRangeAddress = cellRangeAddress;this.beginRow = beginRow;this.mergeRows = mergeRows;}@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {if (CollectionUtil.isNotEmpty(cellRangeAddress)) {if (row.getRowNum() >= beginRow && row.getRowNum() <= beginRow + mergeRows) {for (CellRangeAddress item : cellRangeAddress) {writeSheetHolder.getSheet().addMergedRegionUnsafe(item);}}}}}

因此合并时循环每一条数据判断是否与上一条数据是否相同,相同时再逐条进行合并,写法使用XSSFSheet写法或使用easyExcel自带的两种合并方法:

private void mergeSameRow(WriteSheetHolder writeSheetHolder, int curRowIndex, int needMergeNum, int curColIndex) {Sheet sheet = writeSheetHolder.getSheet();CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - needMergeNum - 1, curRowIndex - 1, curColIndex, curColIndex);sheet.addMergedRegionUnsafe(cellAddresses);// 或使用// sheet.addMergedRegion(cellAddresses);
}

合并策略的使用:

public void download(HttpServletResponse response) {/***  此处为业务数据****/String subject = "XXX";String fileName = subject.concat(".xlsx");//定义多级表头List<List<String>> headList = new ArrayList<>();//定义数据体List<List<Object>> dataList = new ArrayList<>();this.getHeadAndData(headList, dataList);// 定义合并单元格 (根据业务需求自定义)List<Integer> mergeColumnIndex = new ArrayList<>();int mergeCol = headList.size() - offerVOList.size() - 3;for (int i = 0; i < mergeCol; i++) {mergeColumnIndex.add(i);}for (int j = mergeCol + 3; j < headList.size(); j++) {mergeColumnIndex.add(j);}// 生成自定义策略EasyExcelCustomMergeStrategy customMergeStrategy = new EasyExcelCustomMergeStrategy(mergeColumnIndex.stream().filter(Objects::nonNull).mapToInt(i->i).toArray());customMergeStrategy.setTotalNum(dataList.size());try {response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));EasyExcel.write(response.getOutputStream()).excelType(ExcelTypeEnum.XLSX).head(headList).sheet(subject).registerWriteHandler(customMergeStrategy)//调用策略.doWrite(dataList);} catch (Exception e) {throw new Exception(String.format("生成文件(%s)失败", fileName), e);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/185831.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据服务化在京东平台API接口接入的实践

数据服务化在京东的实践 导读 本次分享的主题为数据服务化在京东的实践&#xff0c;主要包含三个模块&#xff1a;数据服务化的缘起、成长、如何将系统做得更好。 01 缘起&#xff1a;数据服务化从 0 到 1 1. 缘起 京东数据智能部负责维护数据资产和对外提供数据服务&#…

设置chunk自动扩展到多大

1. 设置chunk自动扩展 execute function task(modify chunk extendable on,8); 2. 设置dbs扩展到多大合适 execute function task(modify space sp sizes,testdb1024,1024,10240) testdb 初始1MB 下次扩1MB 最大10MB

护眼台灯选购注意事项?考公专用护眼台灯推荐

随着科技的发展和进步&#xff0c;台灯的外观也不在和以往一般笨重&#xff0c;而是变得越来越美观&#xff0c;而且也更方便了&#xff0c;功能也越来越多元化了。台灯作为现在我们日常学习、阅读、办公必备的一盏照明灯具&#xff0c;其光源的舒适度是非常重要的。那么挑选台…

C语言第入门——第十六课

目录 一、分治策略与递归 二、递归 1.求解n的阶乘 2.输入整数、倒序输出 3.输入整数、正序输出 4.计算第n位Fibonacci数列 ​编辑5.无序整数数组打印 6.找到对应数组下标 一、分治策略与递归 在我们遇到大问题的时候&#xff0c;我们的正确做法是将它分解成小问题&a…

机械人必须要了解的丝杆螺母参数

丝杆螺母是机械中重要的零部件之一&#xff0c;主要用于将旋转运动转化为直线运动&#xff0c;或者将直线运动转化为旋转运动。只有正确了解丝杆螺母的参数&#xff0c;才能进行选型。 1、螺纹规格&#xff1a;丝杆螺母的螺纹规格是按照国家标准进行分类的&#xff0c;常见的有…

15.“百钱百鸡“问题

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码1程序运行代码优化程序运行代码优化进阶 前言 本系列为结构循环编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 二、题目分析 三、解题 程序运行代码1 #include <stdio.h> int main…

洗袜子的洗衣机哪款好?内衣洗衣机测评

随着人们的生活水平的提升&#xff0c;越来越多小伙伴来开始追求更高的生活水平&#xff0c;一些智能化的小家电就被发明出来&#xff0c;而且内衣洗衣机是其中一个。现在通过内衣裤感染到细菌真的是越来越多&#xff0c;所以我们对内衣裤的清洗频次会高于普通衣服&#xff0c;…

LVGL案例分享--手把手教你移植到T113-i国产工业开发板

市面上有许多嵌入式GUI库可供选择&#xff0c;包括开源GUI库和闭源GUI库&#xff0c;开源GUI库&#xff1a;LVGL&#xff0c;EmWin等&#xff1b;闭源GUI库&#xff1a;TouchGFX&#xff0c;柿饼GUI等。 本篇文章主要描述如何将LVGL8.1移植到创龙科技的T113-i的开发板上。 LV…

几种典型的深度学习算法:(CNN、RNN、GANS、RL)

以下是几种典型的深度学习算法&#xff1a; 1、卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;&#xff1a;主要用于图像和视频等视觉任务。通过使用卷积层、池化层和全连接层等不同类型的神经网络层&#xff0c;CNN 可以学习图像中的特征并…

unity中的模型坐标系与3dmax导出的模型坐标系不一致的解决方案

unity中的模型坐标系与3dmax导出的模型坐标系不一致的解决方案 unity是左手坐标系&#xff0c;3dmax为右手坐标系 需要在3dmax中修改坐标系 顶视图中改成&#xff1a;X轴&#xff08;红色&#xff09;向右&#xff1a; Y轴&#xff08;蓝色&#xff09;朝向自己: Z轴&#xff…

Python大数据之linux学习总结——day09_hive函数

hive函数 函数分类标准[重点] 知识点: 原生分类标准: 内置函数 和 用户定义函数(UDF,UDAF,UDTF)分类标准扩大化: 本来&#xff0c;UDF 、UDAF、UDTF这3个标准是针对用户自定义函数分类的&#xff1b; 但是&#xff0c;现在可以将这个分类标准扩大到hive中所有的函数&#…

为什么选择CodeEase?

目录 为什么选择CodeEase核心功能后端前端 框架结构总结 为什么选择CodeEase CodeEase是一个标准化的低代码平台 愿景 我们励志开发一站式服务&#xff0c;缩短网站开发周期&#xff0c;降低程序bug率&#xff0c;减少开发人力和成本&#xff0c;推出了多租户SaaS平台开发模板…