当前位置:

首页 > 编程开发 > Java怎么使用EasyExcel进行单元格合并

Java怎么使用EasyExcel进行单元格合并

1.项目场景:简介:报销单导出要根据指定的excel模板去自动替换对应,然后重新生成一份新的excel。在给定的excel模板中,有部分字段进行了单元格合并,如下所示。2.问题描述由于一张报销单可能存在多条报销内容,可以看到,当超过模板中预先给定的一条时,则会自动换行,但换行时并不会自动依照模板中的样式进行单元格合并,如下所示。3.原因分析:首先可以直观的看到excel进行数据插入并自动换行的时候,换行的数据并没有按照上一行的样式进行自动合并。于是便想着用代码把这几列手动合并,然后再加上边框样式就可以解决

本文目录

    1.项目场景:

    简介:报销单导出要根据指定的excel模板去自动替换对应,然后重新生成一份新的excel。在给定的excel模板中,有部分字段进行了单元格合并,如下所示。

    Java怎么使用EasyExcel进行单元格合并

    2.问题描述

    由于一张报销单可能存在多条报销内容,可以看到,当超过模板中预先给定的一条时,则会自动换行,但换行时并不会自动依照模板中的样式进行单元格合并,如下所示。

    Java怎么使用EasyExcel进行单元格合并

    3.原因分析:

    首先可以直观的看到excel进行数据插入并自动换行的时候,换行的数据并没有按照上一行的样式进行自动合并。
    于是便想着用代码把这几列手动合并,然后再加上边框样式就可以解决了。

    4.解决方案:

    1. 需要注意的是,按照以上的思路,直接进行单元格合并,然后加上边框并不能直接解决问题。

    2. 需要将后边空的每一个单元格先创建出来,然后将其一块合并才可以解决,创建单元格代码在下方CustomCellWriteHandler类中说明。

    这也算是耗费一整天时间踩的坑。。。

    public static void outExcelBalance(String modelFile, String newFile, Map map, List fillData, HttpServletResponse response, String fileName){
            //定义model模板中默认的行数
            int firstRow = 7; //excel中表示第八行,即模板中默认的一条
            int lastRow = 7;
            InputStream is = null;
            File file = new File(modelFile);
            File file1 = new File(newFile);
            //String file1Name = file1.getName();
            BufferedInputStream bis = null;
            try {
                if (!file.exists()) {
                    copyFileUsingJava7Files(file, file1);
                }
    
                //TODO 单元格样式
                Set rowsBorderSet= new HashSet<>();
                CustomCellWriteHandler customCellWriteHandler = null;
    
                //TODO 单元格合并
                List cellRangeAddresss = new ArrayList<>();
    
                if (ListUtils.isNotNull(fillData)){
                    if (fillData.size() > 1){
                        //合并每条报销单的第3-10列
                        for (int i = 1; i < fillData.size(); i++) {
                            firstRow++;
                            lastRow++;
    
                            cellRangeAddresss.add(new CellRangeAddress(firstRow, lastRow, 2, 9));
                            cellRangeAddresss.add(new CellRangeAddress(firstRow, lastRow, 10, 11));
    
                            rowsBorderSet.add(firstRow);
                        }
                    }
                }
                customCellWriteHandler = new CustomCellWriteHandler(rowsBorderSet);
                MyMergeStrategy myMergeStrategy = new MyMergeStrategy(cellRangeAddresss);
    
                ExcelWriter excelWriter = EasyExcel.write(newFile)
                        //注册单元格式
                        .registerWriteHandler(customCellWriteHandler)
                        //注册合并策略
                        .registerWriteHandler(myMergeStrategy)
                        .withTemplate(modelFile).build();
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                if (!ListUtil.listIsEmpty(fillData)){
                    excelWriter.fill(fillData, fillConfig, writeSheet);
                    //excelWriter.fill(fillData, fillConfig, writeSheet);
                }
                excelWriter.fill(map, writeSheet);
                excelWriter.finish();
                response.setHeader("content-type", "text/plain");
                response.setHeader("content-type", "application/x-msdownload;");
                response.setContentType("text/plain; charset=utf-8");
                response.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes("utf-8"),"ISO8859-1"));
                byte[] buff = new byte[1024];
    
                OutputStream os = null;
                os = response.getOutputStream();
                bis = new BufferedInputStream(new FileInputStream(file1));
                int i = bis.read(buff);
    
                while (i != -1) {
                    os.write(buff, 0, buff.length);
                    os.flush();
                    i = bis.read(buff);
                }
            }
            catch (Exception e){
                LOGGER.error(e.getMessage());
            }
            finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                // 删除生成文件
                    /*if (file1.exists()) {
                        file1.delete();
                    }*/
            }
        }

    单元格合并MyMergeStrategy类代码:

    public class MyMergeStrategy extends AbstractMergeStrategy {
    
        //合并坐标集合
        private List cellRangeAddresss;
    
        //构造
        public MyMergeStrategy(List cellRangeAddresss) {
            this.cellRangeAddresss = cellRangeAddresss;
        }
    
        @Override
        protected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {
            if (ListUtils.isNotNull(cellRangeAddresss)) {
                if (cell.getRowIndex() == 7 ) {
                    for (CellRangeAddress item : cellRangeAddresss) {
                        sheet.addMergedRegionUnsafe(item);
                    }
                }
            }
        }
    }

    单元格样式CustomCellWriteHandler类代码:

    public class CustomCellWriteHandler implements CellWriteHandler {
        private static final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class);
    
        //标黄行宽集合
        private Set rowIndexs;
    
        //构造
        public CustomCellWriteHandler(Set rowIndexs) {
            this.rowIndexs = rowIndexs;
        }
    
        public CustomCellWriteHandler() {
        }
    
        @Override
        public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
            LOGGER.info("beforeCellCreate~~~~");
        }
    
        @Override
        public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
            LOGGER.info("afterCellCreate~~~~");
        }
    
        @Override
        public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {
    
        }
    
        @Override
        public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    
              //获取工作簿
    //        HSSFWorkbook wb = new HSSFWorkbook();
    //        //获取sheet
    //        HSSFSheet sheet = wb.createSheet();
    //        HSSFRow row = sheet.createRow();
    //        HSSFCellStyle style = wb.createCellStyle();
    
            // 这里可以对cell进行任何操作
            if (CollectionUtils.isNotEmpty(rowIndexs)) {
                Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
                CellStyle cellStyle = workbook.createCellStyle();
    
                Sheet sheet = writeSheetHolder.getSheet();
                cellStyle.setAlignment(new HSSFWorkbook().createCellStyle().getAlignment());
                cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
                cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
                cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
                cellStyle.setBorderTop(BorderStyle.THIN);//上边框
                cellStyle.setBorderRight(BorderStyle.THIN);//右边框
                cellStyle.setWrapText(true);//自动换行
    
                //字体
    //            Font cellFont = workbook.createFont();
    //            cellFont.setBold(true);
    //            cellStyle.setFont(cellFont);
    //            //标黄,要一起设置
    //            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); //设置前景填充样式
    //            cellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());//前景填充色
    
                if (rowIndexs.contains(cell.getRowIndex())) {
                    Row row = null;
                    //循环创建空白单元格
                    for (int i = 0; i < rowIndexs.size(); i++) {
                        for (Integer rowIndex : rowIndexs){
                            //创建4-10列的空白格
                            row = sheet.getRow(rowIndex.intValue());
                            if (row == null){
                                row = sheet.createRow(rowIndex.intValue());
                            }
                            for (int j = 3; j <= 9; j++) {
                                //获取8行的cell列
                                cell = row.createCell(j);
                                cell.setCellStyle(cellStyle);
                                cell.setCellValue(" ");
                                LOGGER.info("第{}行,第{}列创建空白格。", cell.getRowIndex(), j);
                            }
                            //创建12列的红白格
                            cell = row.createCell(11);
                            cell.setCellStyle(cellStyle);
                            cell.setCellValue(" ");
                            LOGGER.info("第{}行,第11列创建空白格。", cell.getRowIndex());
                            //创建21列的空白格
                            cell = row.createCell(21);
                            cell.setCellStyle(cellStyle);
                            cell.setCellValue(" ");
                            LOGGER.info("第{}行,第21列创建空白格。", cell.getRowIndex());
                        }
                    }
                }
            }
        }
    }

    5.总结

    核心步骤:

    1. 
    	//创建单元格样式
    	CustomCellWriteHandler customCellWriteHandler = new CustomCellWriteHandler(参数按需给定);
    2. 
    	//单元格进行合并
    	List cellRangeAddresss = new ArrayList<>();
    	//例如:从firstRow行到lastRow行的2列到9列合并
        cellRangeAddresss.add(new CellRangeAddress(firstRow, lastRow, 2, 9));
        cellRangeAddresss.add(new CellRangeAddress(firstRow, lastRow, 10, 11));
        MyMergeStrategy myMergeStrategy = new MyMergeStrategy(cellRangeAddresss);
    3. 
    	//注册以上两种策略
    	ExcelWriter excelWriter = EasyExcel.write(newFile)
        //注册单元格式
        .registerWriteHandler(customCellWriteHandler)
        //注册合并策略
        .registerWriteHandler(myMergeStrategy)
        .withTemplate(modelFile).build();

    说明:刚开始修复的时候,并没有想过后边每个空的单元格需要先创建出来,才可以进行合并。一直以为是工具类的问题,后来不断的翻阅解决方案,看到有说需要先进行创建空白单元格,然后再进行合并,最终完美解决了。

    关于代码部分,由于是业务代码,中间夹杂了许多不需要的。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。