• 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
    789");
     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;
        }
    }
    

    微信公众号
    在这里插入图片描述

  • 相关阅读:
    PJzhang:CVE-2020-1472微软NetLogon权限提升漏洞~复现
    PJzhang:vulnhub靶机sunset系列SUNSET:DECOY
    PJzhang:vulnhub靶机sunset系列SUNSET:TWILIGHT
    PJzhang:vulnhub靶机sunset系列SUNSET:SUNRISE
    PJzhang:vulnhub靶机sunset系列SUNSET:MIDNIGHT
    PJzhang:vulnhub靶机sunset系列SUNSET:SOLSTICE
    PJzhang:whatweb指纹扫描工具样例
    HTTP状态码解读
    【并发】线程与进程的区别
    MYSQL之性能优化 ----MySQL性能优化必备25条
  • 原文地址:https://www.cnblogs.com/niugang0920/p/12515722.html
Copyright © 2020-2023  润新知