需求:
将上图表头合并为下图
技术选型:
原本框架里使用的Easyexcel,简单调查了一下并不能符合,就基于poi手撸合并逻辑呗
代码:
逻辑:
- createHeader: 方法入口
- countHeader: 统计表头信息,列宽,行高。headerIndexList存放每个格子的宽度,用于之后合并单元格
- fillHeader: 根据headerIndexList填充表头信息到headerList
- mergedRegion: 合并单元格
//表头文字数组
private List<List<Columns>> headerList = new ArrayList<>();
//表头数量数组
private List<List<Integer>> headerIndexList = new ArrayList<>();
/**
* 创建表头
* @param columns
*/
private void createHeader(List<Columns> columns){
//创建头样式
XSSFCellStyle cellStyle = PoiExcelStyle.getDefaultCellTitleStyle(workbook);
//统计表头相关信息
countHeader(columns,0);
//拼表头
if (headerIndexList.size() > 1){
for (int i = 0;i < headerIndexList.size() - 1;i++){
fillHeader(i,i + 1);
}
}
//列宽 = 最后一行的列表长度
colNum = headerIndexList.get(headerIndexList.size() - 1).size();
String[] args;
//遍历创建行
for (int i = 0;i < headerIndexList.size();i++){
args = new String[colNum];
for (int j = 0,n = 0;j < headerIndexList.get(i).size();j++){
int len = n + headerIndexList.get(i).get(j);
while (n < len) {
args[n++] = headerList.get(i).get(j).getLabel();
}
}
//创建行
createRowCells((short) 600,cellStyle,args);
}
//合并单元格
dp = new int[headerIndexList.size()];
loc = new int[headerIndexList.size()];
mergedRegion(0,colNum);
}
//动态一下
//记录每行当前长度
private int[] dp;
//记录每行当前位置
private int[] loc;
/**
* 合并表头单元格
* @param deep
* @param len
*/
private void mergedRegion(int deep,int len){
//合并同类型单元格 sheet.addMergedRegion(new CellRangeAddress(开始行, 结束行, 开始列, 结束列))
for (int i = loc[deep];(i < headerIndexList.get(deep).size() && len > 0);i++) {
int l = headerIndexList.get(deep).get(i);
len -= l;
if (l == 1 && deep == indexRow - 1){
dp[deep]++;
loc[deep]++;
}else if (l == 1 && deep != indexRow - 1 && (headerList.get(deep).get(i).getChildren() == null || headerList.get(deep).get(i).getChildren().size() == 0)) {
sheet.addMergedRegion(new CellRangeAddress(deep,indexRow - 1, dp[deep], dp[deep]));
for (int d = deep;d < dp.length;d++){
dp[d]++;
loc[d]++;
}
}else if (l >= 1){
if (l > 1) sheet.addMergedRegion(new CellRangeAddress(deep,deep , dp[deep], dp[deep] + l - 1));
dp[deep] += l;
loc[deep]++;
mergedRegion(deep + 1,l);
}
}
}
/**
* 填充头信息
* @param p
* @param c
*/
private void fillHeader(int p,int c){
for (int i = 0,j = 0;i < headerIndexList.get(p).size();i++){
if (headerIndexList.get(p).get(i) == 1 && (headerList.get(p).get(i).getChildren() == null || headerList.get(p).get(i).getChildren().size() == 0)){
headerIndexList.get(c).add(j,1);
headerList.get(c).add(j++,headerList.get(p).get(i));
}else{
int n = headerIndexList.get(p).get(i);
do{
n -= headerIndexList.get(c).get(j++);
}while (n > 0);
}
}
}
/**
* 统计header信息
* @param columns
* @param tier
* @return
*/
private int countHeader(List<Columns> columns,int tier){
if (headerIndexList.size() == tier)
headerIndexList.add(new ArrayList<>());
if (headerList.size() == tier)
headerList.add(new ArrayList<>());
int num = 0;
for (Columns item : columns){
//存入Header label
headerList.get(tier).add(item);
if (item.getChildren() == null || item.getChildren().size() == 0){
propKeys.add(item.getProperty());
num += 1;
headerIndexList.get(tier).add(1);
}else {
int result = countHeader(item.getChildren(),tier + 1);
headerIndexList.get(tier).add(result);
num += result;
}
}
return num;
}