java使用EasyExcel實(shí)現(xiàn)合并單元格
官方默認(rèn)提供了兩個(gè)單元格合并策略OnceAbsoluteMerge
及LoopMergeStrategy
。OnceAbsoluteMerge
只能實(shí)現(xiàn)一次合并,如果需要多次合并,則需要注冊(cè)多次服務(wù)。LoopMergeStrategy
可以實(shí)現(xiàn)循環(huán)合并,但是只能實(shí)現(xiàn)每隔 n 個(gè)單元格合并。
如圖,還有另外一個(gè)靜態(tài)類(lèi) AbstractMergeStrategy
是兩者的父類(lèi),可以據(jù)此,自己實(shí)現(xiàn)合并方案。
合并策略
package cn.com.hmsm.bif.xingwen.accounting; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.merge.AbstractMergeStrategy; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Cell; 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; /** * 指定列,如果相鄰兩行的數(shù)據(jù)相同,則合并為一行 */ @Slf4j public class ColumnSameDataMergeStrategy extends AbstractMergeStrategy { private final int mergeRowIndex; public ColumnSameDataMergeStrategy(int mergeRowIndex) { this.mergeRowIndex = mergeRowIndex; } @Override protected void merge( Sheet sheet, Cell cell, Head head, Integer relativeRowIndex ) { int rowIndex = cell.getRowIndex(); int colIndex = cell.getColumnIndex(); // 必須這樣使用,不然lastRow 永遠(yuǎn)為空 sheet = cell.getSheet(); Row lastRow = sheet.getRow(rowIndex - 1); if (lastRow == null) { log.warn("rowIndex is {}, lastRow is null , data is {} ", rowIndex, cell); return; } Cell lastRowCell = lastRow.getCell(colIndex); // 先進(jìn)行列的判斷,再進(jìn)行的判斷 if (colIndex != mergeRowIndex) { return; } // log.info("rowIndex is {}, data is {}", rowIndex, cell.toString()); // 如果數(shù)據(jù)相同,才進(jìn)行合并 if (!lastRowCell.getStringCellValue().equals(cell.getStringCellValue())) { return; } // 檢查當(dāng)前單元格是否已在合并中,如果在,則修改合數(shù)據(jù);如果不存在,新建 List<CellRangeAddress> list = sheet.getMergedRegions(); for (int i = 0, listSize = list.size(); i < listSize; i++) { CellRangeAddress cellAddresses = list.get(i); // 檢查列是否相同 if (cellAddresses.getFirstColumn() != colIndex || cellAddresses.getLastColumn() != colIndex) { continue; } // 因?yàn)閿?shù)據(jù)是從上到下添加的,行數(shù)是依次遞增的,所以只需要檢查合并的最后一行和上一行是否相同即可。 if (cellAddresses.getLastRow() == lastRowCell.getRowIndex()) { // 見(jiàn)最后一行,改為當(dāng)前行 cellAddresses.setLastRow(rowIndex); sheet.removeMergedRegion(i); sheet.addMergedRegion(cellAddresses); return; } } // 不存在已合并的區(qū)域,新建合格合并區(qū)域 CellRangeAddress cra = new CellRangeAddress(lastRowCell.getRowIndex(), cell.getRowIndex(), lastRowCell.getColumnIndex(), cell.getColumnIndex()); sheet.addMergedRegion(cra); } }
使用
try (ExcelWriter excelWriter = EasyExcel.write(filePath).withTemplate(templateFileName).build()) { WriteSheet writeSheet = EasyExcel.writerSheet("對(duì)賬單") .registerWriteHandler(new ColumnSameDataMergeStrategy(0)) .build(); Map<String, Object> map = MapUtils.newHashMap(); map.put("title", title); map.put("time", reconciliationCycle); excelWriter.fill(map, writeSheet); // 這里注意 入?yún)⒂昧薴orceNewRow 代表在寫(xiě)入list的時(shí)候不管list下面有沒(méi)有空行 都會(huì)創(chuàng)建一行,然后下面的數(shù)據(jù)往后移動(dòng)。默認(rèn) 是false,會(huì)直接使用下一行,如果沒(méi)有則創(chuàng)建。 // forceNewRow 如果設(shè)置了true,有個(gè)缺點(diǎn) 就是他會(huì)把所有的數(shù)據(jù)都放到內(nèi)存了,所以慎用 // 簡(jiǎn)單的說(shuō) 如果你的模板有l(wèi)ist,且list不是最后一行,下面還有數(shù)據(jù)需要填充 就必須設(shè)置 forceNewRow=true 但是這個(gè)就會(huì)把所有數(shù)據(jù)放到內(nèi)存 會(huì)很耗內(nèi)存 FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); excelWriter.fill(byStoreDtoList, fillConfig, writeSheet); // excelWriter.fill(byStoreDtoList, writeSheet); }
注意,在使用時(shí),依賴(lài)的是框架提供的數(shù)據(jù),需要設(shè)置 forceNewRow
為 true
,將所有數(shù)據(jù)放入內(nèi)存中,一次保存。否則會(huì)出現(xiàn)問(wèn)題。
模板
效果圖
到此這篇關(guān)于java使用EasyExcel實(shí)現(xiàn)合并單元格的文章就介紹到這了,更多相關(guān)java EasyExcel合并單元格內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring的@ConfigurationProperties注解詳解
這篇文章主要介紹了Spring的@ConfigurationProperties注解詳解,@ConfigurationProperties該注解是用來(lái)獲取yml或者properties配置文件的配置信息,下面根據(jù)一些配置信息給出案例代碼進(jìn)行講解,需要的朋友可以參考下2023-11-11Java Scanner輸入兩個(gè)數(shù)組的方法
今天小編就為大家分享一篇Java Scanner輸入兩個(gè)數(shù)組的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07如何用Java將數(shù)據(jù)庫(kù)的數(shù)據(jù)生成pdf返回給前端用戶下載
本文詳細(xì)介紹了使用SpringBoot、iText庫(kù)、MyBatis等技術(shù)從數(shù)據(jù)庫(kù)中選取數(shù)據(jù)并生成PDF文件的后端處理流程,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09java學(xué)習(xí)之一維數(shù)組中重復(fù)元素的去除
關(guān)于一維數(shù)組中有重復(fù)的元素該怎么剔除,作為java初學(xué)者的我整理出不調(diào)用任何特殊庫(kù)的基礎(chǔ)方法,這種思想在其他語(yǔ)言也適用,有需要的朋友可以借鑒參考下2021-09-09SpringBoot實(shí)現(xiàn)自定義注解用于文件驗(yàn)證的詳細(xì)過(guò)程(大小、擴(kuò)展名、MIME類(lèi)型)
SpringBoot,Spring Cloud中經(jīng)常需要處理文件上傳的功能,為了確保上傳的文件滿足特定的要求(如擴(kuò)展名、MIME類(lèi)型和文件大小),我們可以創(chuàng)建一個(gè)自定義注解來(lái)簡(jiǎn)化驗(yàn)證過(guò)程,需要的朋友可以參考下2024-08-08Java計(jì)算器核心算法代碼實(shí)現(xiàn)
今天小編就為大家分享一篇關(guān)于Java計(jì)算器核心算法代碼實(shí)現(xiàn),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01實(shí)現(xiàn)java文章點(diǎn)擊量記錄實(shí)例
這篇文章主要為大家介紹了實(shí)現(xiàn)java文章點(diǎn)擊量記錄實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Java的CyclicBarrier循環(huán)屏障解析
這篇文章主要介紹了Java的CyclicBarrier循環(huán)屏障解析,CyclicBarrier和CountDownLatch一樣,是一個(gè)同步工具類(lèi),它允許一組線程相互等待直到達(dá)到某個(gè)common?barrier?point,在程序中CyclicBarrier是非常有用的,它適用于一組線程必須互相等待的情況,需要的朋友可以參考下2023-12-12詳解Java中super的幾種用法并與this的區(qū)別
這篇文章主要介紹了Java中super的幾種用法并與this的區(qū)別,有需要的朋友可以參考一下2013-12-12mybatis高級(jí)映射一對(duì)多查詢(xún)實(shí)現(xiàn)代碼
本篇文章主要介紹了mybatis高級(jí)映射一對(duì)多查詢(xún)實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04