Java导出Execl疑难点处理的实现

网友投稿 323 2022-12-09


Java导出Execl疑难点处理的实现

一.背景

最近业务需求需要导出Execl,最终做出的效果如下,中间牵扯到大量的数据计算。

二.疑难问题分析

问题1:跨单元格处理及边框设置

问题2:自定义背景颜色添加

问题3:单元格中部分文字设置颜色

问题4:高度自适应处理

三.问题解决

在处理整个Excel导出中总结了很多。

整个开发过程使用的是Apache POI

pom.xml

org.apache.poi

poi-ooxml

3.8

org.apache.poi

poi-scratchpad

3.8

3.1 HSSFworkbook,XSSFworkbook选哪个

最开始我沿用的是之前开发用的,HSSFworkbook最后发现,HSSFworkbook在处理,自定义单元格背景颜色比较复杂,最后换成了XSSFworkbook。

HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls;

XSSFWorkbook:是操作Excel2007后的版本,扩展名是.xlsx;

所以在这里我推荐使用XSSFWorkbook

3.2跨单元格及边框设置

//创建第一行,索引是从0开始的

row = sheet.createRow(0);

//创建第一个单元格

XSSFCell cell0 = row.createCell(0);

//设置单元格文字

cell0.setCellValue("姓名");

//设置单元格样式

cell0.setCellStyle(cellStyleHead);

//跨单元格设置

//参数为 firstRow, lastRow, firstCol, lastCol

CellRangeAddress cellRange1 = new CellRangeAddress(0, 1, 0, 0);

sheet.addMergedRegion(cellRange1);

//注意如果直接在下面写设置边框的样式,可能会出现边框覆盖不全的情况,可能是样式覆盖问题

//所以应该在数据渲染完成之后,在代码的最后写跨单元格边框设置,这是非常重要的

调用设置边框

//在数据渲染完成,调用封装的边框设置方法

setRegionStyle(wb, sheet, cellRange1);

设置边框方法

/**

* 合并单元格之后设置边框

*

* @param wb XSSFWorkbook对象

* @param sheet sheet

* @param region region

*/

static void setRegionStyle(XSSFWorkbook wb, XSSFSheet sheet, CellRangeAddress region) {

RegionUtil.setBorderTop(1, region, sheet, wb);

RegionUtil.setBorderBottom(1, region, sheet, wb);

RegionUtil.setBorderLeft(1, region, sheet, wb);

RegionUtil.setBorderRight(1, region, sheet, wb);

}

3.3自定义背景颜色设置

因为poi自带的颜色索引可能不满足我们开发的需求,需要自定义样色

//创建单元格样式

XSSFCellStyle cellStyleContent = wb.createCellStyle();

//创建背景颜色 226, 239, 218 对应的就是RGB颜色 红绿蓝

cellStyleContent.setFillForegroundColor(new XSSFColor(new java.awt.Color(226, 239, 218)));

//填充m

cellStyleContent.setFillPattern(CellStyle.SOLID_FOREGROUND);

3.4设置单元格中部分字体颜色

XSSFRichTextString ts = new XSSFRichTextString("123456\r\n789");

XSSFFont font2 = wb.createFont();

//字体高度

font2.setFontHeightInPoints((short) 10);

// 字体

font2.setFontName("宋体");

//字体颜色

font2.setColor(HSSFColor.GREEN.index);

//那些字体要设置颜色,

//int startIndex 开始索引

//int endIndex 结束索引

// Font font 字体

ts.applyFont(5, ts.length(), font2);

3.5高度自适应

封装的工具类如下,需要在数据渲染完的每行,调用如下工具类

//高度自适应

//XSSFRow row;

ExcelUtil.calcAndSetRowHeigt(row);

import org.apache.poi.ss.usermodel.Cell;

import org.apache.poi.ss.util.CellRangeAddress;

import org.apache.poi.xssf.usermodel.*;

import java.util.HashMap;

import java.util.Map;

/**

* @author Created by niugang on 2020/3/13/13:34

*/

public class ExcelUtil {

private ExcelUtil() {

throw new IllegalStateException("Utility class");

}

/**

* 根据行内容重新计算行高

*

* @param sourceRow sourceRow

*/

public static void calcAndSetRowHeigt(XSSFRow sourceRow) {

for (int cellIndex = sourceRow.getFirstCellNum(); cellIndex <= sourceRow.getPhysicalNumberOfCells(); cellIndex++) {

//行高

double maxHeight = sourceRow.getHeight();

XSSFCell sourceCell = sourceRow.getCell(cellIndex);

//单元格的内容

String cellContent = getCellContentAsString(sourceCell);

if (null == cellContent || "".equals(cellContent)) {

continue;

}

//单元格的宽高及单元格信息

Map cellInfoMap = getCellInfo(sourceCell);

Integer cellWidth = (Integer) cellInfoMap.get("width");

Integer cellHeight = (Integer) cellInfoMap.get("height");

if (cellHeight > maxHeight) {

maxHeight = cellHeight;

}

XSSFCellStyle cellStyle = sourceCell.getCellStyle();

//sourceRow.getSheet().getWorkbook()

XSSFFont font = cellStyle.getFont();

//字体的高度

short fontHeight = font.getFontHeight();

//cell内容字符串总宽度

double cellContentWidth = cellContent.getBytes().length * 2 * 256;

//字符串需要的行数 不做四舍五入之类的操作

double stringNeedsRows = cellContentWidth / cellWidth;

//小于一行补足一行

if (stringNeedsRows < 1.0) {

stringNeedsRows = 1.0;

}

//需要的高度 (Math.floor(stringNeedsRows) - 1) * 40 为两行之间空白高度

double stringNeedsHeight = (double) fontHeight * stringNeedsRows;

//需要重设行高

if (stringNeedsHeight > maxHeight) {

maxHeight = stringNeedsHeight;

//超过原行高三倍 则为5倍 实际应用中可做参数配置

if (maxHeight / cellHeight > 5) {

maxHeight = 5 * cellHeight;

}

//最后取天花板防止高度不够

maxHeight = Math.ceil(maxHeight);

//重新设置行高 同时处理多行合并单元格的情况

Boolean isPartOfRowsRegion = (Boolean) cellInfoMap.get("isPartOfRowsRegion");

if (isPartOfRowsRegion.equals(Boolean.TRUE)) {

Integer firstRow = (Integer) cellInfoMap.get("firstRow");

Integer lastRow = (Integer) cellInfoMap.get("lastRow");

//平均每行需要增加的行高

double addHeight = (maxHeight - cellHeight) / (lastRow - firstRow + 1);

for (int i = firstRow; i <= lastRow; i++) {

double rowsRegionHeight = sourceRow.getSheet().getRow(i).getHeight() + addHeight;

rowsRegionHeight=rowsRegionHeight+10;

sourceRow.getSheet().getRow(i).setHeight((short) rowsRegionHeight);

}

} else {

maxHeight=maxHeight+10;

sourceRow.setHeight((short) maxHeight);

}

}

}

}QRKtLXUwxP

/**

* 解析一个单元格得到数据

*

* @param cell cell

* @return String

*/

private static String getCellContentAsString(XSSFCell cell) {

final String strZero =".0";

if (null == cell) {

return "";

}

String result = "";

switch (cell.getCellType()) {

case Cell.CELL_TYPE_NUMERIC:

String s = String.valueOf(cell.getNumericCellValue());

if (s != null) {

if (s.endsWith(strZero)) {

s = s.substring(0, s.length() - 2);

}

}

result = s;

break;

case Cell.CELL_TYPE_STRING:

result = String.valueOf(cell.getStringCellValue()).trim();

break;

case Cell.CELL_TYPE_BLANK:

break;

case Cell.CELL_TYPE_BOOLEAN:

result = String.valueOf(cell.getBooleanCellValue());

break;

case Cell.CELL_TYPE_ERROR:

break;

default:

break;

}

return result;

}

/**

* 获取单元格及合并单元格的宽度

*

* @param cell cell

* @return Map

*/

private static Map getCellInfo(XSSFCell cell) {

XSSFSheet sheet = cell.getSheet();

int rowIndex = cell.getRowIndex();

int columnIndex = cell.getColumnIndex();

boolean isPartOfRegion = false;

int firstColumn = 0;

int lastColumn = 0;

int firstRow = 0;

int lastRow = 0;

int sheetMergeCount = sheet.getNumMergedRegions();

for (int i = 0; i < sheetMergeCount; i++) {

CellRangeAddress ca = sheet.getMergedRegion(i);

firstColumn = ca.getFirstColumn();

lastColumn = ca.getLastColumn();

firstRow = ca.getFirstRow();

lastRow = ca.getLastRow();

if (rowIndex >= firstRow && rowIndex <= lastRow) {

if (columnIndex >= firstColumn && columnIndex <= lastColumn) {

isPartOfRegion = true;

break;

}

}

}

Map map = new HashMap<>(16);

int width = 0;

int height = 0;

boolean isPartOfRowsRegion = false;

if (isPartOfRegion) {

for (int i = firstColumn; i <= lastColumn; i++) {

width += sheet.getColumnWidth(i);

}

for (int i = firstRow; i <= lastRow; i++) {

height += sheet.getRow(i).getHeight();

}

if (lastRow > firstRow) {

isPartOfRowsRegion = true;

}

} else {

width = sheet.getColumnWidth(columnIndex);

height += cell.getRow().getHeight();

}

map.put("isPartOfRowsRegion", isPartOfRowsRegion);

map.put("firstRow", firstRow);

map.put("lastRow", lastRow);

map.put("width", width);

map.put("height", height);

return map;

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:基于Freemarker和xml实现Java导出word
下一篇:IDEA搭建dubbo项目的过程及存在的问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~