EasyPOI 根据模板导出excel时,无法自适应行高得解决方案记录

EasyPOI 根据模板导出excel时,无法自适应行高得解决方案记录

首先说一下问题得场景

  • 我这里时用来做PDF 得生成得 ,先使用Excel 模板生成excel,再将excel流转为pdf,但是问题也就产生了,就是如果行高不自适应得话会导致内容得丢失。这点我做了好多的处理也不好使,最先想到的是使用Excel的模板设置 ,但是不会生效,很遗憾。或者通过EasyPOI来设置Style,也没有设置行高的选项(我是使用模板进行转换的)。所以也是没有办法了,常规通用的方法已经不能实现需求了,只能是查看EasyPOI源码来哪个地方没有自适应,并对其做相应的更改。上一波模板。
  • 在这里插入图片描述
  • 这里的这三个字段可能会过长需要换行并且调整行高,都是字符串String类型的数据。

第一步

在这里插入图片描述

第二步

在这里插入图片描述

第三步

在这里插入图片描述

  • 到这里就发现代码比较复杂了,但是不要慌,很容易我们就能找到对模板进行赋值操作的是这个方法parseTemplate(wb.getSheetAt(i),map,params.isColForEash()),所以我们继续点,嘿嘿!

第四步

在这里插入图片描述

  • 这一步很清晰的知道赋值代码是setValueForCellByMap(row.getCell(i),map);

第五步

在这里插入图片描述

  • 这里我们看到有好几处对单元格进行赋值,经过我测试debug发现上边的都不是对循环进行赋值的地方只有下边的代码是
        //判断foreach 这种方法
        if (oldString != null && oldString.contains(FOREACH)) {
            addListDataToExcel(cell, map, oldString.trim());
        }

所以继续点。

第六步

在这里插入图片描述

  • 我们能看到两处地方使用了这个 方法,我们点进去看看。

第七步 (就是这个方法!)

setForEeachRowCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns, map,rowspan, colspan, mergedRegionHelper)
 private void setForEeachRowCellValue(boolean isCreate, Row row, int columnIndex, Object t,
                                         List<ExcelForEachParams> columns, Map<String, Object> map,
                                         int rowspan, int colspan,
                                         MergedRegionHelper mergedRegionHelper) throws Exception {
        //所有的cell创建一遍
        for (int i = 0; i < rowspan; i++) {
            int size = columns.size();//判断是不是超出设置了
            for (int j = columnIndex, max = columnIndex + colspan; j < max; j++) {
                if (row.getCell(j) == null) {
                    row.createCell(j);
                    CellStyle style = row.getRowNum() % 2 == 0
                            ? getStyles(false,
                            size >= j - columnIndex ? null : columns.get(j - columnIndex))
                            : getStyles(true,
                            size >= j - columnIndex ? null : columns.get(j - columnIndex));
                    //返回的styler不为空时才使用,否则使用Excel设置的,更加推荐Excel设置的样式
                    if (style != null) {
                        row.getCell(j).setCellStyle(style);
                    }
                }

            }
            if (i < rowspan - 1) {
                row = row.getSheet().getRow(row.getRowNum() + 1);
            }
        }
        //填写数据
        ExcelForEachParams params;
        row = row.getSheet().getRow(row.getRowNum() - rowspan + 1);
        for (int k = 0; k < rowspan; k++) {
            int ci = columnIndex;//cell的序号

            short high=columns.get(0).getHeight();
            int n=k;
            while (n>0) {
                if ( columns.get(n * colspan).getHeight()==0) {
                    n--;
                } else {
                    high= columns.get(n * colspan).getHeight();
                    break;
                }
            }
            row.setHeight(high);
            for (int i = 0; i < colspan && i < columns.size(); i++) {
                boolean isNumber = false;
                params = columns.get(colspan * k + i);
                tempCreateCellSet.add(row.getRowNum() + "_" + (ci));
                if (params == null) {
                    continue;
                }
                if (StringUtils.isEmpty(params.getName())
                        && StringUtils.isEmpty(params.getConstValue())) {
                    row.getCell(ci).setCellStyle(params.getCellStyle());
                    ci = ci + params.getColspan();
                    continue;
                }
                String val = null;
                Object obj = null;
                //是不是常量
                if (StringUtils.isEmpty(params.getName())) {
                    val = params.getConstValue();
                } else {
                    String tempStr = new String(params.getName());
                    if (isNumber(tempStr)) {
                        isNumber = true;
                        tempStr = tempStr.replaceFirst(NUMBER_SYMBOL, "");
                    }
                    map.put(teplateParams.getTempParams(), t);
                    obj = eval(tempStr, map);
                    val = obj.toString();
                }
                if (obj != null  && obj instanceof ImageEntity) {
                    ImageEntity img = (ImageEntity)obj;
                    row.getCell(ci).setCellValue("");
                    createImageCell(row.getCell(ci),img.getHeight(),img.getUrl(),img.getData());
                }else if (isNumber && StringUtils.isNotEmpty(val)) {
                    row.getCell(ci).setCellValue(Double.parseDouble(val));
                    row.getCell(ci).setCellType(Cell.CELL_TYPE_NUMERIC);
                } else {
                    try {
                        row.getCell(ci).setCellValue(val);
                    } catch (Exception e) {
                        LOGGER.error(e.getMessage(), e);
                    }
                }
                row.getCell(ci).setCellStyle(params.getCellStyle());    // plq modify at 2017-11-13
                //判断这个属性是不是需要统计
                if (params.isNeedSum()) {
                    templateSumHandler.addValueOfKey(params.getName(), val);
                }
                //如果合并单元格,就把这个单元格的样式和之前的保持一致
                setMergedRegionStyle(row, ci, params);
                //合并对应单元格
                if ((params.getRowspan() != 1 || params.getColspan() != 1)
                        && !mergedRegionHelper.isMergedRegion(row.getRowNum() + 1, ci)) {
                    PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),
                                    row.getRowNum() + params.getRowspan() - 1, ci,
                                    ci + params.getColspan() - 1);
                }
                ci = ci + params.getColspan();
            }
            row = row.getSheet().getRow(row.getRowNum() + 1);
        }

    }
  • 通过查看前面代码的模式可以看出,或者可以debug得出这里是对字符串赋值的地方。
    在这里插入图片描述上边那两个是图片和数字赋值的地方。所以我们需要在这里进行更改就好了。进入细节。大家要注意奥!上一波我得代码。方便大家复制参考我上代码,不是图片
 private void setForEeachRowCellValue(boolean isCreate, Row row, int columnIndex, Object t,
                                         List<ExcelForEachParams> columns, Map<String, Object> map,
                                         int rowspan, int colspan,
                                         MergedRegionHelper mergedRegionHelper) throws Exception {
        //所有的cell创建一遍
        for (int i = 0; i < rowspan; i++) {
            int size = columns.size();//判断是不是超出设置了
            for (int j = columnIndex, max = columnIndex + colspan; j < max; j++) {
                if (row.getCell(j) == null) {
                    row.createCell(j);
                    CellStyle style = row.getRowNum() % 2 == 0
                            ? getStyles(false,
                            size >= j - columnIndex ? null : columns.get(j - columnIndex))
                            : getStyles(true,
                            size >= j - columnIndex ? null : columns.get(j - columnIndex));
                    //返回的styler不为空时才使用,否则使用Excel设置的,更加推荐Excel设置的样式
                    if (style != null) {
                        row.getCell(j).setCellStyle(style);
                    }
                }

            }
            if (i < rowspan - 1) {
                row = row.getSheet().getRow(row.getRowNum() + 1);
            }
        }
        //填写数据
        ExcelForEachParams params;
        row = row.getSheet().getRow(row.getRowNum() - rowspan + 1);
        List<CellRangeAddress> cellRangeAddressList = getCombineCellList(row.getSheet());
        for (int k = 0; k < rowspan; k++) {
            int ci = columnIndex;//cell的序号

            short high=columns.get(0).getHeight();
            int n=k;
            while (n>0) {
                if ( columns.get(n * colspan).getHeight()==0) {
                    n--;
                } else {
                    high= columns.get(n * colspan).getHeight();
                    break;
                }
            }
            row.setHeight(high);
            for (int i = 0; i < colspan && i < columns.size(); i++) {
                boolean isNumber = false;
                params = columns.get(colspan * k + i);
                tempCreateCellSet.add(row.getRowNum() + "_" + (ci));
                if (params == null) {
                    continue;
                }
                if (StringUtils.isEmpty(params.getName())
                        && StringUtils.isEmpty(params.getConstValue())) {
                    row.getCell(ci).setCellStyle(params.getCellStyle());
                    ci = ci + params.getColspan();
                    continue;
                }
                String val = null;
                Object obj = null;
                //是不是常量
                if (StringUtils.isEmpty(params.getName())) {
                    val = params.getConstValue();
                } else {
                    String tempStr = new String(params.getName());
                    if (isNumber(tempStr)) {
                        isNumber = true;
                        tempStr = tempStr.replaceFirst(NUMBER_SYMBOL, "");
                    }
                    map.put(teplateParams.getTempParams(), t);
                    obj = eval(tempStr, map);
                    val = obj.toString();
                }
                if (obj != null  && obj instanceof ImageEntity) {
                    ImageEntity img = (ImageEntity)obj;
                    row.getCell(ci).setCellValue("");
                    createImageCell(row.getCell(ci),img.getHeight(),img.getUrl(),img.getData());
                }else if (isNumber && StringUtils.isNotEmpty(val)) {
                    row.getCell(ci).setCellValue(Double.parseDouble(val));
                    row.getCell(ci).setCellType(Cell.CELL_TYPE_NUMERIC);
                } else {
                    try {
                        int valLength = 0;
                        Map<String,Object> cellMap = isCombineCell(cellRangeAddressList,row.getCell(ci),row.getSheet());
                        // 合并的单元格列数
                        int mergeCellCount = ObjectUtils.isEmpty(cellMap.get("mergedCol"))? 0 : (Integer) cellMap.get("mergedCol");
                        char [] strs = val.toCharArray();
                        // 如果是汉字则为 两个字符
                        for (char e : strs) {
                            if(String.valueOf(e).matches("[\u4e00-\u9fa5]")){
                                valLength = valLength +2;
                            }else {
                                valLength = valLength + 1;
                            }
                        }
                        // 获取单元格宽
                        int width = (row.getSheet().getColumnWidth(row.getCell(ci).getColumnIndex()) / 256) +1 ;
                        // 获取单元格高度
                        BigDecimal height = new BigDecimal(row.getHeight()).divide(new BigDecimal(256));
                        // 如果该单元格被合并了
                        if(mergeCellCount > 0){
                            // 获取合并单元格宽度
                            for(int j = 1 ;j < mergeCellCount ; j++){
                                width = width + (row.getSheet().getColumnWidth(row.getCell(ci+j).getColumnIndex()) / 256) +1;
                            }
                        }
                        //向下取整
                        if (width * height.setScale(0,BigDecimal.ROUND_DOWN).intValue() < valLength) {
                            row.setHeight(new BigDecimal(256).multiply(height) .multiply(new BigDecimal((valLength / width) + 1)).shortValue());
                        }
                        row.getCell(ci).setCellValue(val);

                    } catch (Exception e) {
                        LOGGER.error(e.getMessage(), e);
                    }
                }
                row.getCell(ci).setCellStyle(params.getCellStyle());    // plq modify at 2017-11-13
                //判断这个属性是不是需要统计
                if (params.isNeedSum()) {
                    templateSumHandler.addValueOfKey(params.getName(), val);
                }
                //如果合并单元格,就把这个单元格的样式和之前的保持一致
                setMergedRegionStyle(row, ci, params);
                //合并对应单元格
                if ((params.getRowspan() != 1 || params.getColspan() != 1)
                        && !mergedRegionHelper.isMergedRegion(row.getRowNum() + 1, ci)) {
                    PoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),
                                    row.getRowNum() + params.getRowspan() - 1, ci,
                                    ci + params.getColspan() - 1);
                }
                ci = ci + params.getColspan();
            }
            row = row.getSheet().getRow(row.getRowNum() + 1);
        }

    }

这里解释一下代码。先说几个问题。

  • 首先是第一个问题就是根据什么来调整行高?当然是根据文字的长度,但是这里有一个问题就是中文字符的宽度和数字和英文字符的长度不是一样的长度,我们这里假设一个中文字符宽度等于两个其他字符的宽度。
  • 第二我们通过POI方法row.getSheet().getColumnWidth(row.getCell(ci).getColumnIndex()来获取单元格宽度是一个什么样数字这里我debug到这里的时候发现是一个很大数值,这个数值除以256 等与一个字符的长度!!!!! 这是重点哦,这样我们才能完成后边的计算。
  • 第三如果我们合并了单元格,但是POI获取宽度的方法只会获取第一个单元格的宽度,并不是实际单元格的长度。因此我们需要考虑合并单元格的问题。先上两个方法再解释。
 /**
     * 判断cell是否为合并单元格,是的话返回合并行数和列数(只要在合并区域中的cell就会返回合同行列数,但只有左上角第一个有数据)
     * @param listCombineCell  上面获取的合并区域列表
     * @param cell
     * @param sheet
     * @return
     * @throws Exception
     */
    public  static Map<String,Object> isCombineCell(List<CellRangeAddress> listCombineCell,Cell cell,Sheet sheet)
            throws Exception{
        int firstC = 0;
        int lastC = 0;
        int firstR = 0;
        int lastR = 0;
        String cellValue = null;
        Boolean flag=false;
        int mergedRow=0;
        int mergedCol=0;
        Map<String,Object> result=new HashMap<>();
        result.put("flag",flag);
        for(CellRangeAddress ca:listCombineCell)
        {
            //获得合并单元格的起始行, 结束行, 起始列, 结束列
            firstC = ca.getFirstColumn();
            lastC = ca.getLastColumn();
            firstR = ca.getFirstRow();
            lastR = ca.getLastRow();
            //判断cell是否在合并区域之内,在的话返回true和合并行列数
            if(cell.getRowIndex() >= firstR && cell.getRowIndex() <= lastR)
            {
                if(cell.getColumnIndex() >= firstC && cell.getColumnIndex() <= lastC)
                {
                    flag=true;
                    mergedRow=lastR-firstR+1;
                    mergedCol=lastC-firstC+1;
                    result.put("flag",true);
                    result.put("mergedRow",mergedRow);
                    result.put("mergedCol",mergedCol);
                    break;
                }
            }
        }
        return result;
    }


    //获取合并单元格集合
    public static List<CellRangeAddress> getCombineCellList(Sheet sheet) {
        List<CellRangeAddress> list = new ArrayList<>();
        //获得一个 sheet 中合并单元格的数量
        int sheetmergerCount = sheet.getNumMergedRegions();
        //遍历所有的合并单元格
        for(int i = 0; i<sheetmergerCount;i++)
        {
            //获得合并单元格保存进list中
            CellRangeAddress ca = sheet.getMergedRegion(i);
            list.add(ca);
        }
        return list;
    }

这两个方法 下边的是获取该sheet页所有合并单元格的对象。上边的方法是获取返回该位置被合并单元格的地址,长几个格高几个格!!!! 重点啊.。在我们的主方法上我们可以看到我在上边的地方我调用方法获取了一个该sheet所有合并单元格集合的方法
在这里插入图片描述
然后在核心改动的地方调用了获取单元格合并大小的方法
在这里插入图片描述
该方法返回一个Map,有两个主要字段就是mergeColmergedRow的字段,是合并的单元格数量。
然后后边就是计算该行行高应该扩展到多大。然后最后set我们的值就结束了。

最后说一下怎么更改源码。首先我们把这个类复制出来,然后建一个与这个类同样目录的地方。名字也要一样。上一波图片。
在这里插入图片描述
到这里EasyPOI改源码的过程就结束了,有问题的可以讨论哦,然后还有就是这是公司项目我不能发到git上 抱歉啦!

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值