相信很多同学都用过EasyExcel做过导出功能,现在阿Q也碰到一个需求,就是要把导出的Excel中内容相同的纵向单元格给合并,然后还需要给Excel设置动态标题,再进行格式定义调整,现将这个功能的实现过程记录下,给同学们做个参考避免踩到同样的坑。

jar包版本

一、最初版本

导出的结果:

 对应实体类代码:

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentLoopMerge;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.*;import java.io.Serializable;@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Data
@ContentRowHeight(30)
@HeadRowHeight(40)
@ColumnWidth(25)
public class StudentExportVo implements Serializable {private static final long serialVersionUID = -5809782578272943999L;@ExcelProperty(value = "学校", order = 1)@ContentLoopMerge(eachRow = 3)private String school;@ExcelProperty(value = "姓名", order = 2)private String name;@ExcelProperty(value = "性别", order = 3)private String sex;@ExcelProperty(value = "年龄", order = 4)private String age;@ExcelProperty(value = "城市", order = 5)private String city;@ExcelProperty(value = "备注", order = 6)private String remarks;}

对应业务代码:

@ApiOperation(value = "导出学生信息", notes = "导出学生信息")
@PostMapping("/exportStudent")
public void exportStudent(@RequestBody String str, HttpServletResponse response) {List<StudentExportVo> list = this.getStudentExportVos();ExcelUtils.writeExcel(response, StudentExportVo.class, list, "导出学生信息", "sheet1");
}
//数据制造
private List<StudentExportVo> getStudentExportVos() {List<StudentExportVo> list = new ArrayList<>();StudentExportVo v1 = new StudentExportVo();v1.setSchool("北京大学");v1.setName("张三");v1.setSex("男");v1.setAge("20");v1.setCity("北京");v1.setRemarks("无");list.add(v1);StudentExportVo v2 = new StudentExportVo();v2.setSchool("北京大学");v2.setName("李四");v2.setSex("男");v2.setAge("22");v2.setCity("上海");v2.setRemarks("无");list.add(v2);StudentExportVo v3 = new StudentExportVo();v3.setSchool("北京大学");v3.setName("王五");v3.setSex("女");v3.setAge("22");v3.setCity("青岛");v3.setRemarks("无");list.add(v3);StudentExportVo v4 = new StudentExportVo();v4.setSchool("清华大学");v4.setName("赵六");v4.setSex("女");v4.setAge("21");v4.setCity("重庆");v4.setRemarks("无");list.add(v4);StudentExportVo v5 = new StudentExportVo();v5.setSchool("武汉大学");v5.setName("王强");v5.setSex("男");v5.setAge("24");v5.setCity("长沙");v5.setRemarks("无");list.add(v5);StudentExportVo v6 = new StudentExportVo();v6.setSchool("武汉大学");v6.setName("赵燕");v6.setSex("女");v6.setAge("21");v6.setCity("深圳");v6.setRemarks("无");list.add(v6);StudentExportVo v7 = new StudentExportVo();v7.setSchool("厦门大学");v7.setName("陆仟");v7.setSex("女");v7.setAge("21");v7.setCity("广州");v7.setRemarks("无");list.add(v7);return list;
}

二、使用注解的版本

导出的结果:

  对应实体类代码:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Data
@ContentRowHeight(30)
@HeadRowHeight(40)
@ColumnWidth(25)
public class StudentExportVo implements Serializable {private static final long serialVersionUID = -5809782578272943999L;@ExcelProperty(value = {"学生信息","学校"}, order = 1)@ContentLoopMerge(eachRow = 3)private String school;@ExcelProperty(value = {"学生信息","姓名"}, order = 2)private String name;@ExcelProperty(value = {"学生信息","性别"}, order = 3)private String sex;@ExcelProperty(value = {"学生信息","年龄"}, order = 4)private String age;@ExcelProperty(value = {"学生信息","城市"}, order = 5)private String city;@ExcelProperty(value = {"学生信息","备注"}, order = 6)private String remarks;}

对应业务代码:同上

注意:

@ContentLoopMerge(eachRow = 3) 可以合并单元格,但是他是按指定行数合并,并不能实现内容相同的合并

@ExcelProperty(value = {"学生信息","备注"},能实现多个标题,但标题是固定的,不是动态的

三、自定义改造

导出的结果:

对应实体类代码:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Data
@ContentRowHeight(30)
@HeadRowHeight(40)
@ColumnWidth(25)
public class StudentExportVo implements Serializable {private static final long serialVersionUID = -5809782578272943999L;@ExcelProperty(value = {"学生信息","学校"}, order = 1)//@ContentLoopMerge(eachRow = 3)private String school;@ExcelProperty(value = {"学生信息","姓名"}, order = 2)private String name;@ExcelProperty(value = {"学生信息","性别"}, order = 3)private String sex;@ExcelProperty(value = {"学生信息","年龄"}, order = 4)private String age;@ExcelProperty(value = {"学生信息","城市"}, order = 5)private String city;@ExcelProperty(value = {"学生信息","备注"}, order = 6)private String remarks;}

对应业务代码:

@ApiOperation(value = "导出学生信息", notes = "导出学生信息")@PostMapping("/exportStudent")public void exportStudent(@RequestBody String dynamicTitle, HttpServletResponse response) {List<StudentExportVo> list = this.getStudentExportVos();try {String fileName = "学生信息";fileName = URLEncoder.encode(fileName, "UTF-8");response.setContentType("application/json;charset=utf-8");response.setCharacterEncoding("utf-8");response.addHeader("Pargam", "no-cache");response.addHeader("Cache-Control", "no-cache");response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");ServletOutputStream output = response.getOutputStream();//需要合并的列int[] mergeColumeIndex = {0};// 从第二行后开始合并int mergeRowIndex = 2;//设置动态标题List<List<String>> headers = this.getHeaders("学生信息" + dynamicTitle);// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为白色headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);/*WriteFont headWriteFont = new WriteFont();headWriteFont.setFontHeightInPoints((short)20);headWriteCellStyle.setWriteFont(headWriteFont);*/// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定//contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);// 背景绿色//contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());//设置 自动换行contentWriteCellStyle.setWrapped(true);//设置 垂直居中contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//设置 水平居中contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);/*WriteFont contentWriteFont = new WriteFont();// 字体大小contentWriteFont.setFontHeightInPoints((short)20);contentWriteCellStyle.setWriteFont(contentWriteFont);*/// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);EasyExcel.write(output, StudentExportVo.class).sheet("学生信息").head(headers).registerWriteHandler(new ExcelMergeHandler(mergeRowIndex, mergeColumeIndex)).registerWriteHandler(horizontalCellStyleStrategy)
//                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(30)).registerWriteHandler(new AbstractColumnWidthStyleStrategy() {@Overrideprotected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {Sheet sheet = writeSheetHolder.getSheet();int columnIndex = cell.getColumnIndex();if(columnIndex == 5){// 列宽100sheet.setColumnWidth(columnIndex, 10000);}else {// 列宽50sheet.setColumnWidth(columnIndex, 5000);}// 行高40sheet.setDefaultRowHeight((short) 4000);}}).doWrite(list);output.flush();} catch (IOException e) {log.error(e.getMessage(), e);}}private List<List<String>> getHeaders(String dynamicTitle) {List<List<String>> headers=new ArrayList<>();List<String> schoolHead=new ArrayList<>();schoolHead.add(dynamicTitle);schoolHead.add("学校");List<String> nameHead=new ArrayList<>();nameHead.add(dynamicTitle);nameHead.add("姓名");List<String> sexHead=new ArrayList<>();sexHead.add(dynamicTitle);sexHead.add("性别");List<String> ageHead=new ArrayList<>();ageHead.add(dynamicTitle);ageHead.add("年龄");List<String> cityHead=new ArrayList<>();cityHead.add(dynamicTitle);cityHead.add("城市");List<String> remarksHead=new ArrayList<>();remarksHead.add(dynamicTitle);remarksHead.add("备注");headers.add(schoolHead);headers.add(nameHead);headers.add(sexHead);headers.add(ageHead);headers.add(cityHead);headers.add(remarksHead);return headers;}//数据制造private List<StudentExportVo> getStudentExportVos() {List<StudentExportVo> list = new ArrayList<>();StudentExportVo v1 = new StudentExportVo();v1.setSchool("北京大学");v1.setName("张三");v1.setSex("男");v1.setAge("20");v1.setCity("北京");v1.setRemarks("无");list.add(v1);StudentExportVo v2 = new StudentExportVo();v2.setSchool("北京大学");v2.setName("李四");v2.setSex("男");v2.setAge("22");v2.setCity("上海");v2.setRemarks("无");list.add(v2);StudentExportVo v3 = new StudentExportVo();v3.setSchool("北京大学");v3.setName("王五");v3.setSex("女");v3.setAge("22");v3.setCity("青岛");v3.setRemarks("无");list.add(v3);StudentExportVo v4 = new StudentExportVo();v4.setSchool("清华大学");v4.setName("赵六");v4.setSex("女");v4.setAge("21");v4.setCity("重庆");v4.setRemarks("无");list.add(v4);StudentExportVo v5 = new StudentExportVo();v5.setSchool("武汉大学");v5.setName("王强");v5.setSex("男");v5.setAge("24");v5.setCity("长沙");v5.setRemarks("无");list.add(v5);StudentExportVo v6 = new StudentExportVo();v6.setSchool("武汉大学");v6.setName("赵燕");v6.setSex("女");v6.setAge("21");v6.setCity("深圳");v6.setRemarks("无");list.add(v6);StudentExportVo v7 = new StudentExportVo();v7.setSchool("厦门大学");v7.setName("陆仟");v7.setSex("女");v7.setAge("21");v7.setCity("广州");v7.setRemarks("无");list.add(v7);return list;}

合并单元格相同内容处理类:ExcelMergeHandler

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
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;
public class ExcelMergeHandler implements CellWriteHandler {private int[] mergeColumnIndex;private int mergeRowIndex;public ExcelMergeHandler() {}public ExcelMergeHandler(int mergeRowIndex, int[] mergeColumnIndex) {this.mergeRowIndex = mergeRowIndex;this.mergeColumnIndex = mergeColumnIndex;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> 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]) {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.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();// 将当前单元格数据与上一个单元格数据比较Boolean dataBool = preData.equals(curData);//此处需要注意:因为我是按照工程名称确定是否需要合并的,所以获取每一行第二列数据和上一行第一列数据进行比较,如果相等合并,getCell里面的值,是工程名称所在列的下标String s1 = cell.getRow().getCell(0).getStringCellValue();String s2 = cell.getSheet().getRow(curRowIndex - 1).getCell(0).getStringCellValue();/*BigDecimal d1 = new BigDecimal(cell.getRow().getCell(0).getNumericCellValue());BigDecimal d2 = new BigDecimal(cell.getSheet().getRow(curRowIndex - 1).getCell(0).getNumericCellValue());*/Boolean bool = s1.compareTo(s2) == 0 ? true:false;// 原始的// Boolean bool =  cell.getRow().getCell(1).getStringCellValue().equals(cell.getSheet().getRow(curRowIndex - 1).getCell(1).getStringCellValue());if (dataBool && bool) {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);}}}
}

总结:

1、使用自定义合并相同内容单元格时,实体类中的注解@ContentLoopMerge需要去掉,要不然会受到影响

2、该段自定义代码中如内容合并、设置动态标题、内容、以及每个列的宽度及字体都是可以根据自定义策略来进行设置的,具体用法可以参考代码注释说明,根据需要进行使用

3、可以参考官网文档获取更多知识内容 https://alibaba-easyexcel.github.io/index.html

最后:
        相信前面两种导出方法同学们都使用的非常熟练,重点是第三种自定义的导出方法,像这种自定义的使用场景还是比较多的,希望到时候开发中遇到类似的场景,把代码拿过去直接用就好,少走些弯路。

EasyExcel合并相同内容单元格及动态标题功能的实现相关推荐

  1. 【知识兔】Excel教程:批量合并相同内容单元格神技

    大家好,今天知识兔跟大家分享一下关于批量合并相同内容单元格的方法有哪些,这是一位同学在群里求助的案例,当时群内针对这个问题各位小伙伴们抛出了多种解决方法,索性知识兔小编就整理成专题文章来跟大家分享一下 ...

  2. javascript根据单元格内容动态合并相同内容单元格

    记录下,按照自己的思维流程写的代码,难免有冗余和不合理的地方,着急要用,也懒得优化,适用于有合并前两列中相同内容单元格需求的地方.如有有人能受到启发写出更加好用完美的代码,那我会感到非常高兴.另外发现 ...

  3. Excel中如何批量合并相同内容单元格

    Excel表格录入数据后,发现一些部门名称重复录入太多,看上去也比较复杂,怎么才能将所有重复内容全部合并到一个单元格中去呢? 最终效果 1.首先,我们要选中需要合并的列,「数据」-「分级显示」-「分类 ...

  4. excel单元格内容拆分_Excel技巧:如何批量合并相同内容单元格?

    金秋十月,现在的天气已经进入一场秋雨一场寒的时候了,小伙伴们要多多注意增减衣服哦 今天,小编将要和大家分享的是如何快速批量合并相同内容的单元格,效果图如下图所示: 那具体要怎么操作才能快速实现这样的效 ...

  5. Excel如何快速合并相同内容单元格

    合并单元格很多小伙伴都做过,但是你知道怎么快速合并多个内容相同的单元格吗?下面就跟大家介绍一下. 1.工具法 1.如下图,是某单位员工信息表,现在老板要求将部门列相同部门单元格合并在一起. 2.点击如 ...

  6. Excel批量合并相同内容单元格操作——WPS太秀了

    昨儿我整理了一篇OFFICE批量化合并相同单元格的博文,觉得好棒棒,结果下午的发现,        WPS居然把这个操作集成成工具了...太秀了呀,给国产良心软件点个赞不过分吧.废话不多说看效果. 待 ...

  7. element ui中table合并相同内容单元格

    一.遍历需要合并的集合,定义spanArr数组 二.在table中增加事件 三.调用objectSpanMethod方法,合并单元格

  8. layui合并表格的单元格 合并列或行的两种方法

    注意:两种方法要求layui表格的单元格中没有使用fixed属性,不然代码不会起效 方法一: /** * 合并单元格(列) * @param res 表格数据 * @param curr 当前页 * ...

  9. 使用EasyExcel 根据单元格数值动态合并单元格

    文章目录 前言 一.实现思路 二.实现步骤 1.拦截策略 2.EasyExcel写操作 前言 使用EasyExcel 根据单元格数值动态合并单元格 开发中遇到一个需求,需要根据Excel表格中单元格的 ...

最新文章

  1. mysql dump 参数_mysqldump常用参数
  2. Gradle 使用技巧(四) - 如何定位和解决依赖冲突
  3. 我可以在同一页面上使用多个版本的jQuery吗?
  4. c语言hailstone序列,第一章 c语言上机试卷.doc
  5. 000-SQL Server
  6. 什么是CouchDB?
  7. java linux web_java在Linux下的web应用(一)
  8. ffmpeg -acodec列表
  9. 基于k8s安装配置kubeflow
  10. 摩尔庄园一直显示服务器,《摩尔庄园手游》一直显示获取远端资源号介绍 进不去怎么办...
  11. network secruity studay day3
  12. (2) python--pandas
  13. CF 1467 C. Three Bags
  14. 轻量应用服务器和ecs云服务器哪个好
  15. win10系统如何关闭服务器,win10命令关闭服务器该怎么操作关闭?
  16. python小白新手所学内容(一)
  17. Linux系统破解root密码
  18. c语言clip函数,深入理解裁剪(clip)
  19. Fight Over Ringtone Firm Heats Up
  20. ajax 实验报告,AJAX实验报告 (4500字).doc

热门文章

  1. 2020iPS细胞研究进展综述
  2. YouBank数字钱包
  3. 重定向--Linux bash
  4. 【毕业设计】flink大数据淘宝用户行为数据实时分析与可视化
  5. 自动化做任务、收能量工具Hamibot,我愿称它为神器
  6. 只要心存美好便是岁月花开时
  7. JQuery返回前一页
  8. 浅谈jodaTime 的使用
  9. Mathematica画图的问题
  10. 揭秘:张国荣葬礼,袁咏仪为何哭到失控?