最近在做图表报表相关的开发时,遇到了一个问题:页面上展示的Excel在下载打开后发现同一类别下的单元格没有合并,是一行一行的状态:
而预计的效果是要和页面上展示的一样:
因为这块展示功能的数据是从数据库中取出来在页面动态展示的,不能确保每次展示的数据都是同一种合并情况,所以想要在后端写死不可能了,接下来分析一下下载合并的需求:
把相同疑点编号的数据行进行合并,合并0~6以及第11列的相同行数据,要求合并的数据必须是在同一疑点下的,即使不同疑点下对应的其他数据列的数据相同也不能合并,了解到以上需求后,接下来就可以开干了。
思路:因为疑点编号是判断合并的依据,因此首先进行第一列的合并,接下来的列都以第一列为准进行合并,下边是对第一列所进行的处理:
public static void mergeSameColumn(Sheet sheet, int column) {
//总行数
int totalRows = sheet.getLastRowNum();
//首次出现行数
int firstRow = 0;
//连续最终出现行数
int lastRow = 0;
boolean flag = false;
List list = new ArrayList<>();
for (int i = 0; i < totalRows; i++) {
String curRowCell = sheet.getRow(i).getCell(column).getStringCellValue();
list.add(curRowCell);
}
System.out.println(list.toString());
for(int j = 0;j < totalRows; j++ ) {
if ((j+1) <= totalRows-1) {
if (list.get(j).equals(list.get(j + 1))) {
if (!flag) {
firstRow = j;
}
lastRow = j + 1;
flag = true;
} else if (!list.get(j).equals(list.get(j + 1))) {
if (flag) {
lastRow = j;
if (lastRow > firstRow) {
sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, column, column));
flag = false;
}
}
}
if (((j + 1) == totalRows) && (lastRow > firstRow)) {
sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, column, column));
}
}
}
}
思路是:初始化首次出现的行数和要合并连续出现的最终行数,这里column参数取0即可,接着拿到第一列值的集合,然后遍历第一列的值(集合),记录值首次出现的位置,直到值不同的时候就把集合的对应关系转换为行的对应关系进行行合并,依次遍历。
第一列处理好后,就可以得到所有第一列的合并单元格,进而拿到所有合并单元格的起始行和最终行,其他行均以这个合并单元格的集合为准,代码如下:
/**
* 合并指定Excel sheet页、指定列中连续相同内容的单元格
* @param sheet Excel sheet
* @param column 指定列
*/
public static void mergeSpecifiedColumn(List combineCell,Sheet sheet, int column) {
for(int k = 0;k integers = analysisResult(combineCell.get(k));
String lastRowCellContent = sheet.getRow(integers.get(0)).getCell(column).getStringCellValue();
sheet.addMergedRegion(new CellRangeAddress(integers.get(0), integers.get(1), column, column));
//Set当前单元格的值
Cell cell = sheet.getRow(integers.get(0)).getCell(column);
cell.setCellValue(lastRowCellContent);
}
}
//获取合并单元格集合
public static List getCombineCellList(Sheet sheet)
{
List list = new ArrayList<>();
//获得一个 sheet 中合并单元格的数量
int sheetmergerCount = sheet.getNumMergedRegions();
//遍历所有的合并单元格
for(int i = 0; i isCombineCell(List listCombineCell){
int firstR = 0;
int lastR = 0;
List result=new ArrayList<>();
for(CellRangeAddress ca:listCombineCell)
{
//获取到固定第一列合并单元格的起始行, 结束行
firstR = ca.getFirstRow();
lastR = ca.getLastRow();
result.add(firstR+":"+lastR);
//String value = String.join(":",String.valueOf(firstR),String.valueOf(lastR));
//result.add(value);
}
return result;
}
//解析获取的合并单元格
public static List analysisResult(String results){
String[] splitValue = results.split(":");
List list = new ArrayList<>();
int counterFirst = Integer.parseInt(splitValue[0]);
int counterLast = Integer.parseInt(splitValue[1]);
list.add(counterFirst);
list.add(counterLast);
return list;
}
原本mergeSpecifiedColumn()方法中是要进行二次的循环处理判断连续相同值的合并单元格的值,因为考虑到值的合不合并只与第一列有关,故而直接取解析合并单元格的起始和最终行进行合并,免去了不少麻烦。
最后,在写好的导出代码下添加以下代码即可,我把获取合并单元格的语句专门放在了处理逻辑中去,这样保证了合并多列的连续相同单元格时不会获取到除第一列以外的合并单元格,方便其他列进行复用合并单元格的起始行和最终行,代码侵入性和通用型都很不错,如下:
Sheet sheet = workbook.getSheet(sheetName);
if(null != sheet) {
mergeSameColumn(sheet, 0);
//获取所有合并单元格
List combineCellList = ExpendResultQueryBOImpl.getCombineCellList(sheet);
//返回合并区域列表
List combineCell = ExpendResultQueryBOImpl.isCombineCell(combineCellList);
mergeSpecifiedColumn(combineCell, sheet, 1);
mergeSpecifiedColumn(combineCell, sheet, 2);
mergeSpecifiedColumn(combineCell, sheet, 3);
mergeSpecifiedColumn(combineCell, sheet, 4);
mergeSpecifiedColumn(combineCell, sheet, 5);
mergeSpecifiedColumn(combineCell, sheet, 6);
mergeSpecifiedColumn(combineCell, sheet, 10);
mergeSpecifiedColumn(combineCell, sheet, 11);
}
导出合并效果: