Java导出Excel多列纵向合并的实现


placeholder image
admin 发布于:2018-04-18 15:30:14
阅读:loading

关于导出Excel的文章写的比较多了,今天来分享一款单元格纵向(常见的表格合并以纵向为多)合并效果的实现。

实现优点

代码简单,调用方便;

支持Jxls模板导出Excel的二次单元格合并(模板导出表示数据量不至于太大);

支持Poi内存导出Excel的批量合并(分批次的进行单元格合并);

支持标题占用多行的情况;

合并前提

所有需要合并列的数据需要提前拍好顺序;

特别注意

在大数据量导出时,数据往往需要以分页的实现进行数据导出,此时的分页不应该是传统意义上的分页,而是以一个最大维度的待合并列作为一组数据为前提的分页,保证多个分页中不会出现单元格合并串行的情况(比如读取的第二页的数据需要还需要与第一页末尾处的数据进行合并等),所以我建议的方式可以是先将待合并区域最大维度去重复然后每次循环读取该维度下的内容进行单元格合并,即上文说的Poi创建Row的方式,处理合并区域也只会在当前的数据范围内进行合并。

Jxls模板导出合并

员工信息表.png

示例中是使用Jxls1的模板实现的导出,合并的列为1、2、3列;标题处占用2行,正式数据从第3行开始。

批量读取数据Poi创建

Excel.png

示例中所有的Row、Cell都是使用Poi的Api创建,除了列宽未设置其它任何样式。本示例假设标题占用5行,实际数据是从第6行开始创建;合并的列同样为前面3列。

核心代码参考

package cn.chendd.example.merger;

 

import java.util.*;

import org.apache.commons.collections.CollectionUtils;

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

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

 

public class CellMergerUtil {

 

   /**

    * <pre>

    * 根据集合数据和需要合并的列索引号返回所有列的所有待合并区域

    * </pre>

    */

   public static Map<Integer, List<CellRangeAddress>> getRanglesByData(

         List<Map<String, Object>> itemList, int[] mergerRows, int startRows) {

      Map<Integer, List<CellRangeAddress>> ranglesMap = new LinkedHashMap<Integer, List<CellRangeAddress>>();

      if(mergerRows == null || mergerRows.length == 0){

         return ranglesMap;

      }

      if(CollectionUtils.isEmpty(itemList)){

         return ranglesMap;

      }

      //根据数据索引号获取数据的合并位置

      for(int i=0 , lens=mergerRows.length ; i < lens ; i++){

         int size = itemList.size();

         for(int j=0 ; j < size ; j++){

            int merger = 0;

            Map<String , Object> item = itemList.get(j);

            String value = getValuesByMergerIndex(item , mergerRows , i);

            for(int k=j+1 ; k < size ; k++){

                Map<String , Object> nextItem = itemList.get(k);

                String nextValue = getValuesByMergerIndex(nextItem , mergerRows , i);

                if(!value.equals(nextValue)){

                   break;

                }

                merger++;//累计需要合并单元格的行数

            }

            //使用索引从0开始的合并

            if(merger >= 1){

                //添加需要处理单元格的位置

                int column = mergerRows[i];

                int firstRow = startRows + j;

                int lastRow = firstRow + merger;

                CellRangeAddress range = new CellRangeAddress(firstRow, lastRow, column, column);

                List<CellRangeAddress> list = ranglesMap.get(column);

                if(list == null){

                   list = new ArrayList<CellRangeAddress>();

                }

                list.add(range);

                ranglesMap.put(column, list);

                j = j + merger;//将待合并的区域数据移至下面合并完后的一行

            }

         }

      }

      return ranglesMap;

   }

  

   //根据值转换为对应索引列的值

   public static String getValuesByMergerIndex(Map<String , Object> item , int[] mergerRows , int i){

      StringBuilder builder = new StringBuilder();

      Object values[] = item.entrySet().toArray();

      for(int t=0 ; t < mergerRows.length ; t++){

         if(t <= i){

            builder.append(values[mergerRows[t]]).append("");

         }

      }

      return builder.toString();

   }

  

   /**

    * <pre>

    * 合并单元格

    * </pre>

    */

   public static void mergerColumns(Sheet sheet, Map<Integer, List<CellRangeAddress>> rangeMap) {

      Set<Entry<Integer , List<CellRangeAddress>>> entrySet = rangeMap.entrySet();

      for (Entry<Integer, List<CellRangeAddress>> entry : entrySet) {

         //合并单元格

         List<CellRangeAddress> valueList = entry.getValue();

         for (CellRangeAddress range : valueList) {

            sheet.addMergedRegion(range);

         }

      }

   }

}

参考下载

运行效果.zip

代码实现.zip



 点赞


 发表评论

当前回复:作者

 评论列表


留言区