背景
現(xiàn)在需要將一個導(dǎo)出列表數(shù)據(jù)到Excel表格的功能進(jìn)行改造,將指定列相同數(shù)據(jù)自動合并單元格。
如上圖所示,指定A、B兩列自動合并,如圖所示(6、7),(8、9),(13、14、15)要自動合并單元格。
EasyExcel 介紹
EasyExcel是一個基于Java的、快速、簡潔、解決大文件內(nèi)存溢出的Excel處理工具。他能讓你在不用考慮性能、內(nèi)存的等因素的情況下,快速完成Excel的讀、寫等功能。
EasyExcel相比其他Excel解析框架(Apache poi和jxl),擁有更好的內(nèi)存消耗管理算法。特別是對07版Excel的解決,EasyExcel重寫了底層解析邏輯,一個3M的Excel解析只需要幾M內(nèi)存,但是用poi解析可能需要100M左右的內(nèi)存。EasyExcel提高了讀取性能,64M內(nèi)存20秒讀取75M的Excel,還有更快的極速模式,但是消耗的內(nèi)存會更多一些。
EasyExcel支持自定義策略合并單元格,可以方便快捷填充數(shù)據(jù)到模板中,有活躍的中文社區(qū)支持,完善的測試用例可以覆蓋大部分業(yè)務(wù)場景的使用。
合并單元格案例講解
使用EasyExcel導(dǎo)出Excel代碼示例:
@Test
public void testWrite() throws IOException {
List<DemoMergeData> resultList = new ArrayList<>();
resultList.add(DemoMergeData.builder().id(1).sub("張勝男").date("12").build());
resultList.add(DemoMergeData.builder().id(1).sub("李四").date("224").build());
resultList.add(DemoMergeData.builder().id(3).sub("王五").date("224").build());
resultList.add(DemoMergeData.builder().id(4).sub("趙柳").date("224").build());
resultList.add(DemoMergeData.builder().id(5).sub("趙柳").date("224").build());
resultList.add(DemoMergeData.builder().id(5).sub("趙柳").date("224").build());
resultList.add(DemoMergeData.builder().id(8).sub("趙柳").date("224").build());
resultList.add(DemoMergeData.builder().id(8).sub("趙柳").date("224").build());
resultList.add(DemoMergeData.builder().id(9).sub("陳琪").date("224").build());
resultList.add(DemoMergeData.builder().id(10).sub("小白").date("241").build());
resultList.add(DemoMergeData.builder().id(11).sub("小黑").date("241").build());
resultList.add(DemoMergeData.builder().id(12).sub("小黑").date("241").build());
resultList.add(DemoMergeData.builder().id(12).sub("小黑").date("241").build());
resultList.add(DemoMergeData.builder().id(12).sub("小黑").date("241").build());
resultList.add(DemoMergeData.builder().id(13).sub("小黑").date("241").build());
// 設(shè)置文件名稱
String fileName = "C:\\Users\\Administrator\\Downloads\\test\\t1.xlsx";
File file = new File(fileName);
if (!file.exists()) {
file.createNewFile();
}
// sheet名稱
EasyExcel.write(fileName, DemoMergeData.class)
.autoCloseStream(Boolean.TRUE)
.sheet("測試導(dǎo)出").doWrite(resultList);
}
導(dǎo)出表格樣式:
自定義策略一:上下行數(shù)據(jù)相同合并單元格
自定義策略一代碼示例:
public class ExcelFillCellMergeStrategy implements CellWriteHandler {
private int[] mergeColumnIndex;
private int mergeRowIndex;
public ExcelFillCellMergeStrategy() {
}
public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
this.mergeRowIndex = mergeRowIndex;
this.mergeColumnIndex = mergeColumnIndex;
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
//當(dāng)前行
int curRowIndex = cell.getRowIndex();
//當(dāng)前列
int curColIndex = cell.getColumnIndex();
if (curRowIndex > mergeRowIndex) {
for (int columnIndex : mergeColumnIndex) {
if (curColIndex == columnIndex) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
}
}
}
}
/**
* 當(dāng)前單元格向上合并
*
* @param cell 當(dāng)前單元格
* @param curRowIndex 當(dāng)前行
* @param curColIndex 當(dāng)前列
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
//獲取當(dāng)前行的當(dāng)前列的數(shù)據(jù)和上一行的當(dāng)前列列數(shù)據(jù),通過上一行數(shù)據(jù)是否相同進(jìn)行合并
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();
// 比較當(dāng)前行的第一列的單元格與上一行是否相同,相同合并當(dāng)前單元格與上一行
if (curData.equals(preData)) {
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);
// 若上一個單元格已經(jīng)被合并,則先移出原有的合并單元,再重新添加合并單元
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);
}
}
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {
}
}
測試代碼示例:
@Test
public void testWrite() throws IOException {
int[] mergeColumnIndex = {0,1};
// 需要從第幾行開始合并
int mergeRowIndex = 1;
// 數(shù)據(jù)就不初始化了
List<DemoMergeData> resultList = new ArrayList<>();
// 設(shè)置文件名稱
String fileName = "C:\\Users\\Administrator\\Downloads\\test\\t1.xlsx";
File file = new File(fileName);
if (!file.exists()) {
file.createNewFile();
}
// sheet名稱
EasyExcel.write(fileName, DemoMergeData.class)
.autoCloseStream(Boolean.TRUE)
.registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex,mergeColumnIndex))
.sheet("測試導(dǎo)出").doWrite(resultList);
}
導(dǎo)出樣式:
自定義策略二:指定列數(shù)據(jù)都相同才合并單元格
自定義策略二代碼示例:
public class MultiColumnMergeStrategy extends AbstractMergeStrategy {
// 合并的列編號,從0開始,指定的index或自己按字段順序數(shù)
private Integer startCellIndex = 0;
private Integer endCellIndex = 0;
// 數(shù)據(jù)集大小,用于區(qū)別結(jié)束行位置
private Integer maxRow = 0;
// 禁止無參聲明
private MultiColumnMergeStrategy() {
}
public MultiColumnMergeStrategy(Integer maxRow, Integer startCellIndex, Integer endCellIndex) {
this.startCellIndex = startCellIndex;
this.endCellIndex = endCellIndex;
this.maxRow = maxRow;
}
// 記錄上一次合并的信息
private final List<List<String>> dataList = new ArrayList<>();
/**
* 每行每列都會進(jìn)入,循環(huán)注意條件限制
*/
@Override
protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) {
int currentCellIndex = cell.getColumnIndex();
int currentRowIndex = cell.getRowIndex();
// 判斷該列是否需要合并
if (currentCellIndex < startCellIndex || currentCellIndex > endCellIndex) {
return;
}
Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
String currentCellValue = curData.toString();
List<String> rowList;
if (dataList.size() > currentRowIndex - 1) {
rowList = dataList.get(currentRowIndex - 1);
} else {
rowList = new ArrayList<>();
dataList.add(rowList);
}
rowList.add(currentCellValue);
// 結(jié)束的位置觸發(fā)下最后一次沒完成的合并
if (relativeRowIndex == (maxRow - 1) && currentCellIndex == endCellIndex) {
System.out.println(JSONObject.toJSONString(dataList));
List<String> tempList = null;
Integer tempIndex = null;
for (int i = 0; i < dataList.size(); i++) {
if (tempList == null) {
tempList = dataList.get(i);
tempIndex = i;
continue;
}
List<String> currList = dataList.get(i);
if (tempList.equals(currList)) {
if (i >= dataList.size() - 1) {
// 結(jié)束的位置觸發(fā)下最后一次沒完成的合并
for (int j = 0; j < tempList.size(); j++) {
sheet.addMergedRegionUnsafe(new CellRangeAddress(tempIndex + 1, i + 1, startCellIndex + j, startCellIndex + j));
}
}
continue;
}
// 當(dāng)前行數(shù)據(jù)和上一行數(shù)據(jù)不同且上面有多行相同數(shù)據(jù)時觸發(fā)合并
if (i - tempIndex > 1) {
for (int j = 0; j < tempList.size(); j++) {
sheet.addMergedRegionUnsafe(new CellRangeAddress(tempIndex + 1, i, startCellIndex + j, startCellIndex + j));
}
}
tempIndex = i;
tempList = currList;
}
}
}
}
測試代碼示例:
@Test
public void testWrite() throws IOException {
// 數(shù)據(jù)就不初始化了
List<DemoMergeData> resultList = new ArrayList<>();
// 設(shè)置文件名稱
String fileName = "C:\\Users\\Administrator\\Downloads\\test\\t1.xlsx";
File file = new File(fileName);
if (!file.exists()) {
file.createNewFile();
}
// sheet名稱
EasyExcel.write(fileName, DemoMergeData.class)
.autoCloseStream(Boolean.TRUE)
.registerWriteHandler(new MultiColumnMergeStrategy(resultList.size(),0,1))
.sheet("測試導(dǎo)出").doWrite(resultList);
}
導(dǎo)出樣式:文章來源:http://www.zghlxwxcb.cn/news/detail-489465.html
總結(jié)
EasyExcel功能靈活強(qiáng)大,可以根據(jù)自身業(yè)務(wù)場景去自定義樣式,也可以使用通過模板填充功能實(shí)現(xiàn)導(dǎo)出國際化語言等復(fù)雜功能。文章來源地址http://www.zghlxwxcb.cn/news/detail-489465.html
到了這里,關(guān)于使用EasyExcel導(dǎo)出表格時合并單元格的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!