Java導出Execl疑難點處理

一.背景

最近業務需求需要導出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;
    }
}

微信公衆號
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章