我们一般导出execl都具有固定的表格形式,但有一天客户对我说如果execl内容超一页后,每次打印的时候,只有第一页有表头,最后一页有表尾,给他们带来很大的麻烦,要求我给他们改一下。
查阅大量的资料结果没有给出具体的方法可以实现改需求,然后我想在导出execl之后对他添加一个固定的表头表尾,但是这个情况就会出现一个问题,需要知到表头表尾的大小和打印时每页的单元格数量,因为不确定单元格是否会出现自动换行,所以需要遍历循环当前所有的单元格,累加后判断是否超出一页。废话不多说,上代码。
//读取所有行
int totalRows = sheet.getPhysicalNumberOfRows();
读取的所有行包括设置格式后的数量,所以要保证导出的模板中没有任何格式,要不然会出现导出实际数量小于查询的数量。
也可以使用下面的方法,会过滤空白的单元行
/**
* @Author haojl
* @Description 读取execl行数
* @Date 2021/10/29 15:14
* @Param [wb]
* @return int
**/
public static int readExeclAllRows(Workbook wb){
Sheet sheet = wb.getSheetAt(0);
CellReference cellReference = new CellReference("A4");
boolean flag = false;
System.out.println("总行数:"+(sheet.getLastRowNum()+1));
for (int i = cellReference.getRow(); i <= sheet.getLastRowNum();) {
Row r = sheet.getRow(i);
if(r == null){
// 如果是空行(即没有任何数据、格式),直接把它以下的数据往上移动
sheet.shiftRows(i+1, sheet.getLastRowNum(),-1);
continue;
}
flag = false;
for(Cell c:r){
if(c.getCellType() != Cell.CELL_TYPE_BLANK){
flag = true;
break;
}
}
if(flag){
i++;
continue;
}else{ //如果是空白行(即可能没有数据,但是有一定格式)
//如果到了最后一行,直接将那一行remove掉
if(i == sheet.getLastRowNum()) {
sheet.removeRow(r);
} else {
//如果还没到最后一行,则数据往上移一行
sheet.shiftRows(i+1, sheet.getLastRowNum(),-1);
}
}
}
System.out.println("总行数:"+(sheet.getLastRowNum()+1));
return sheet.getLastRowNum()+1;
}
正常没有格式的单元格大小为13.5,每页打印的数量为50行,所以我需要去除要添加尾部的大小
/**
* @Author haojl
* @Description 判断当前行是否超过一页
* @Date 2021/10/29 10:15
* @Param [rows, allRows, wb, sheet]
* @return boolean
**/
public static int findLastRow(int rows,int allRows,Workbook wb,Sheet sheet){
int lastRows = 0;
//当前页行高
double AllHeight = 0;
//当前行行高
double height;
for(int i=rows;i<allRows;i++){
height = sheet.getRow(i).getHeight() / 20;
double nextHeight = AllHeight + height;
if(AllHeight <= 500 && nextHeight >= 500){
lastRows = i;
break;
}else if(i == allRows-1){
lastRows = i;
}
AllHeight += height;
}
return lastRows;
}
在插入表尾时需要添加空白行,这样不会使数据丢失
/**
* @Author haojl
* @Description 插入空白行
* @Date 2021/10/28 17:30
* @Param [wb, sheet, starRow, rows]
* @return void
**/
public static void insertRow(HSSFWorkbook wb, HSSFSheet sheet, int starRow,int rows) {
sheet.shiftRows(starRow + 1, sheet.getLastRowNum(), rows,true,false);
starRow = starRow - 1;
for (int i = 0; i < rows; i++) {
HSSFRow sourceRow = null;
HSSFRow targetRow = null;
HSSFCell sourceCell = null;
HSSFCell targetCell = null;
short m;
starRow = starRow + 1;
sourceRow = sheet.getRow(starRow);
targetRow = sheet.createRow(starRow + 1);
targetRow.setHeight(sourceRow.getHeight());
for (m = sourceRow.getFirstCellNum(); m < sourceRow.getLastCellNum(); m++) {
sourceCell = sourceRow.getCell(m);
targetCell = targetRow.createCell(m);
targetCell.setCellStyle(sourceCell.getCellStyle());
targetCell.setCellType(sourceCell.getCellType());
}
}
}
添加表尾的时候要留出下一页表头的行数,这样保证数据正确。我们一般设置模板的时候就会把表头预设,这样通过下面方法把表头信息复制到指定位置
/**
* @Author haojl
* @Description 这个函数是复制行到制定行的,
* 其中第一个参数startRow:为想要复制第一行的标示;
* 第二个参数endRow为: 到此结束复制行的标示,
* 第三个参数pPosition为:复制内容后要放到指定的位置的标示
* @Date 2021/10/27 17:02
* @Param [startRow, endRow, pPosition, sheet]
* @return void
**/
public static void copyRows(int startRow, int endRow, int pPosition, HSSFSheet sheet) {
int pStartRow = startRow - 1;
int pEndRow = endRow - 1;
int targetRowFrom;
int targetRowTo;
int columnCount;
CellRangeAddress region = null;
int i;
int j;
if (pStartRow == -1 || pEndRow == -1) {
return;
}
// 拷贝合并的单元格
for (i = 0; i < sheet.getNumMergedRegions(); i++) {
region = sheet.getMergedRegion(i);
if ((region.getFirstRow() >= pStartRow)
&& (region.getLastRow() <= pEndRow)) {
targetRowFrom = region.getFirstRow() - pStartRow + pPosition;
targetRowTo = region.getLastRow() - pStartRow + pPosition;
CellRangeAddress newRegion = region.copy();
newRegion.setFirstRow(targetRowFrom);
newRegion.setFirstColumn(region.getFirstColumn());
newRegion.setLastRow(targetRowTo);
newRegion.setLastColumn(region.getLastColumn());
sheet.addMergedRegion(newRegion);
}
}
// 设置列宽
for (i = pStartRow; i <= pEndRow; i++) {
HSSFRow sourceRow = sheet.getRow(i);
columnCount = sourceRow.getLastCellNum();
if (sourceRow != null) {
HSSFRow newRow = sheet.createRow(pPosition - pStartRow + i);
newRow.setHeight(sourceRow.getHeight());
for (j = 0; j < columnCount; j++) {
HSSFCell templateCell = sourceRow.getCell(j);
if (templateCell != null) {
HSSFCell newCell = newRow.createCell(j);
copyCell(templateCell, newCell);
}
}
}
}
}
public static void copyCell(HSSFCell srcCell, HSSFCell distCell) {
distCell.setCellStyle(srcCell.getCellStyle());
if (srcCell.getCellComment() != null) {
distCell.setCellComment(srcCell.getCellComment());
}
int srcCellType = srcCell.getCellType();
distCell.setCellType(srcCellType);
if (srcCellType == HSSFCell.CELL_TYPE_NUMERIC) {
if (HSSFDateUtil.isCellDateFormatted(srcCell)) {
distCell.setCellValue(srcCell.getDateCellValue());
} else {
distCell.setCellValue(srcCell.getNumericCellValue());
}
} else if (srcCellType == HSSFCell.CELL_TYPE_STRING) {
distCell.setCellValue(srcCell.getRichStringCellValue());
} else if (srcCellType == HSSFCell.CELL_TYPE_BLANK) {
// nothing21
} else if (srcCellType == HSSFCell.CELL_TYPE_BOOLEAN) {
distCell.setCellValue(srcCell.getBooleanCellValue());
} else if (srcCellType == HSSFCell.CELL_TYPE_ERROR) {
distCell.setCellErrorValue(srcCell.getErrorCellValue());
} else if (srcCellType == HSSFCell.CELL_TYPE_FORMULA) {
distCell.setCellFormula(srcCell.getCellFormula());
} else { // nothing29
}
}
最后一页的时候不要忘记把表尾加上。这样一个自带表头表尾的execl就完成了。
因为我们之前在计算表格行数的时候没有设置格式,这样打印的时候都是没有表格的,下面的方法是设置打印时具有表格的方法。
/**
* @Author haojl
* @Description 设置execl打印时具有网格
* @Date 2021/10/28 9:10
* @Param [wb, sheet, filePath, sheetIndex, startColumn, endColumn, startRow, endRow]
* @return void
**/
public static void printWG(HSSFWorkbook wb,HSSFSheet sheet,String filePath,int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) throws Exception {
//set print area with indexes
wb.setPrintArea(sheetIndex, startColumn, endColumn, startRow, endRow);
//set paper size
sheet.getPrintSetup().setPaperSize(XSSFPrintSetup.A4_PAPERSIZE);
//set display grid lines or not
sheet.setDisplayGridlines(true);
//set print grid lines or not
sheet.setPrintGridlines(true);
FileOutputStream out = new FileOutputStream(new File(filePath));
wb.write(out);
out.close();
System.out.println("xlsx written successfully");
}
我这个方法是在execl没有生成的时候添加的,所以各位看官按照各自的需求进行调整一下。