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

作者:盲目的拾荒者 时间:2021-09-18 12:55:11 

一.背景

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

二.疑难问题分析

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

问题1:跨单元格处理及边框设置
问题2:自定义背景颜色添加
问题3:单元格中部分文字设置颜色
问题4:高度自适应处理

三.问题解决

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

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

pom.xml


<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.8</version>
</dependency>

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<String, Object> 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);
       }
     }

}
 }

/**
  * 解析一个单元格得到数据
  *
  * @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<String  ,    Object>
  */
 private static Map<String, Object> 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<String, Object> 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;
 }
}

来源:https://blog.csdn.net/niugang0920/article/details/104937859

标签:Java,导出,Execl
0
投稿

猜你喜欢

  • 聊聊Redis的单线程模型

    2022-02-21 09:20:42
  • java中String、StringBuffer与StringBuilder的区别

    2021-11-12 13:28:24
  • 使用Mybatis-Plus时的SqlSessionFactory问题及处理

    2022-01-30 07:40:04
  • Java超详细教你写一个银行存款系统案例

    2022-01-04 22:33:59
  • Java栈和基础队列的实现详解

    2023-07-02 05:36:59
  • Tornadofx学习笔记之IconTextFx开源库整合5000+个字体图标

    2023-06-07 12:18:04
  • SpringBoot Profile多环境配置方式

    2023-12-14 01:44:24
  • Java上传文件错误java.lang.NoSuchMethodException的解决办法

    2023-11-10 13:15:43
  • Java中CyclicBarrier 循环屏障

    2021-11-24 08:22:24
  • SpringBoot带你实现一个点餐小程序

    2022-04-01 23:38:08
  • Spring实现处理跨域请求代码详解

    2023-11-25 12:28:34
  • Java异步调用转同步方法实例详解

    2023-11-01 20:38:25
  • C#日期格式强制转换方法(推荐)

    2022-09-19 10:24:57
  • Spring boot2.0 日志集成方法分享(1)

    2023-05-12 20:10:25
  • 一篇文章带你深入了解Java类加载

    2023-06-11 22:12:00
  • SpringMVC中使用@PathVariable绑定路由中的数组的方法

    2023-11-27 14:21:01
  • 教你怎么在IDEA中创建java多模块项目

    2023-05-28 19:25:58
  • Nginx启用压缩及开启gzip 压缩的方法

    2021-09-20 21:19:25
  • springboot aop配合反射统一签名验证实践

    2023-08-04 08:14:07
  • JDK8中新增的原子性操作类LongAdder详解

    2023-06-19 22:02:58
  • asp之家 软件编程 m.aspxhome.com