笔者昨天有个需求,就是把下面的课时信息页签的内容原样导出:
这个地方看似不难,实际后台很复杂,数据的来源也复杂,并不好处理。但是这不是让我纠结的地方。
我纠结的地方是,表头的跨行跨列,而且有的列还是动态的。
有了技术问题的时候,捋捋思路后,如果还解决不了,那可能是要去百度了,因为毕竟是在工作,不要耽误时间,赶紧解决问题。
七拼八凑整理了一套方法(后边附上代码),实现的小时如下:
就这样,表头的问题就解决了。
代码:
package com.incon.project.xmsb.controller;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.Region;
/**
* @方法描述 java中excel导出包括合并单元格和单元格样式
* @初次开发 平传胜
* @开发日期 2019年4月23日下午2:21:05
*/
public class TestPoi2 {
public static void main(String[] args) throws IOException {
try {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("POI-Excel测试");
HSSFCellStyle style = wb.createCellStyle(); // 样式对象
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);// 垂直
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);// 水平
//因为需求一共有3行数据,这里就先创建3个行的空数据,之后分别对这3个行数据进行操作
HSSFRow row1 = sheet.createRow((short) 0);//先创建第一行空数据
HSSFRow row2 = sheet.createRow((short) 1);//先创建第二行空数据
HSSFRow row3 = sheet.createRow((short) 2);//先创建第三行空数据
/** 设置第一行的数据 */
// 四个参数分别是:起始行,起始列,结束行,结束列
//第1个格子,夸3行
sheet.addMergedRegion(new Region(0, (short) 0, 2, (short) 0));
HSSFCell ce1 = row1.createCell((short) 0);
ce1.setCellValue("序号");//表格的第一行第一列显示的数据
ce1.setCellStyle(style);//样式,居中
//第2个格子,夸3行
sheet.addMergedRegion(new Region(0, (short) 1, 2, (short) 1));
HSSFCell ce2 = row1.createCell((short) 1);
ce2.setCellValue("所在学院");
ce2.setCellStyle(style);
//第3个格子,夸3行
sheet.addMergedRegion(new Region(0, (short) 2, 2, (short) 2));
HSSFCell ce3 = row1.createCell((short) 2);
ce3.setCellValue("教师姓名");
ce3.setCellStyle(style);
//第4个格子,夸14列
sheet.addMergedRegion(new Region(0, (short) 3, 0, (short) 16));
HSSFCell ce4 = row1.createCell((short) 3);
ce4.setCellValue("近几年课堂教学学时");
ce4.setCellStyle(style);
/** 设置第二行的数据 */
//第5个格子,夸2行
sheet.addMergedRegion(new Region(1, (short) 3, 2, (short) 3));
HSSFCell ce5 = row2.createCell((short) 3);
ce5.setCellValue("课程名称");
ce5.setCellStyle(style);
//第6个格子,夸2行
sheet.addMergedRegion(new Region(1, (short) 4, 2, (short) 4));
HSSFCell ce6 = row2.createCell((short) 4);
ce6.setCellValue("课程性质");
ce6.setCellStyle(style);
//生成动态的那些行列值
for (int i = 0; i < 6; i++) {
// 计算从那个单元格跨到那一格
int row2_cell_1 = 4;//第二行的第1个值(其实是第4个值,因为前面已经有3列被占用,所以要从第四列开始)
int row2_cell_2 = 5;//第二行的第2个值(其实是第5个值,因为前面已经有4列被占用,所以要从第四列开始)
if (i > 0) {
row2_cell_1 = (i * 2 + 4);//每行夸两列
row2_cell_2 = (i * 2 + 4 + 1);// +1表示下一列的值的取值范围
}
// 单元格合并
sheet.addMergedRegion(new Region(1, (short) (row2_cell_1 + 1), 1, (short) (row2_cell_2 + 1)));
HSSFCell cell = row2.createCell((short) (row2_cell_1 + 1));
cell.setCellValue("201" + i + "秋季"); // 跨单元格显示的数据
cell.setCellStyle(style);
/** 设置第三行的数据 */
// 不跨单元格显示的数据,如:分两行,上一行分别两格为一格,下一行就为两格,“课时”,“人数”
sheet.addMergedRegion(new Region(0, (short) (row2_cell_1 + 1), 0, (short) (row2_cell_2 + 1)));
HSSFCell cell1 = row3.createCell((short) row2_cell_2);
HSSFCell cell2 = row3.createCell((short) (row2_cell_2 + 1));
cell1.setCellValue("课时");
cell1.setCellStyle(style);
cell2.setCellValue("人数");
cell2.setCellStyle(style);
}
//将文件生成到 d 盘根目录下,取名为 workbook.xls
FileOutputStream fileOut = new FileOutputStream("d:/workbook.xls");
wb.write(fileOut);//写出文件
fileOut.close();//关闭流
System.out.print("Excel文件已成功导出,请到D盘根目录查找 workbook.xls 文件!");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
创建格子的代码说明:
其实光看代码就可以了。但是这里有个比较麻烦的地方,需要我们理解。就是 每次计算行的格子的起始位置的问题 很容易错,还不好找原因,下面上图整理下:
说明:
//第4个格子,夸14列
sheet.addMergedRegion(new Region(0, (short) 3, 0, (short) 16));
HSSFCell ce4 = row1.createCell((short) 3);
ce4.setCellValue("近几年课堂教学学时");
ce4.setCellStyle(style);
sheet.addMergedRegion(new Region(1, (short) 3, 2, (short) 3));
HSSFCell ce5 = row2.createCell((short) 3);
ce5.setCellValue("课程名称");
ce5.setCellStyle(style);
代码解释:
new Region(1, (short) 3, 2, (short) 3)
表示:第2行开始,第4列开始,第3行结束,第4列结束
含义是:夸两行,不跨列