EasyExcel实现自定义合并策略

前言

原本是想学习使用Apache的POI的,但是无意中看到Alibaba的开源工具EastExcel,据说比POI更加快速高效,关键是使用起来也简单。

官网地址为:https://alibaba-easyexcel.github.io/index.html,里面讲解地非常清楚易懂,我这里就不再赘述了,只是记录下写表格时如何通过自定义合并策略来实现动态地合并单元格。

入门例子

如果我们不合并单元格,那么下载的样式将是如下这样的:


未合并单元格

那么我们只需要使用如下例子即可:

    @Test
    public void commonWriteTest() {
        String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
        EasyExcel.write(fileName, DownloadDTO.class).sheet("sheet名称")
                .doWrite(getFruitData());
    }
    // 模拟从数据库读取需要下载的列表信息
    private List<DownloadDTO> getFruitData() {
        List<DownloadDTO> returnList = new ArrayList<>();
        DownloadDTO d1 = new DownloadDTO();
        d1.setCategory("水果");
        d1.setFruit("苹果");
        d1.setColor("红色");
        d1.setProduceDate(new Date());

        DownloadDTO d2 = new DownloadDTO();
        BeanUtils.copyProperties(d1, d2);
        d2.setColor("绿色");

        DownloadDTO d3 = new DownloadDTO();
        BeanUtils.copyProperties(d1, d3);
        d2.setColor("白色");

        DownloadDTO t1 = new DownloadDTO();
        t1.setCategory("水果");
        t1.setFruit("香蕉");
        t1.setColor("黄色");
        t1.setProduceDate(new Date());

        DownloadDTO t2 = new DownloadDTO();
        BeanUtils.copyProperties(t1, t2);
        t2.setColor("青色");

        returnList.add(d1);
        returnList.add(d2);
        returnList.add(d3);
        returnList.add(t1);
        returnList.add(t2);
        return returnList;
    }
@Data
public class DownloadDTO {
    @ExcelProperty(value = "物品种类", index = 0)
    private String category;
    @ExcelProperty(value = "水果名称", index = 1)
    private String fruit;
    @ExcelProperty(value = "水果颜色", index = 2)
    private String color;
    @ExcelProperty(value = "水果产期", index = 3)
    private Date produceDate;
}

其中,方法getFruitDataDownloadDTO在后面的例子中还会用到,就不再写出。

如果我们想要合并单元格,那么官方文档上的例子是这样的:


loopMerge

与之对应的代码如下:

    @Test
    public void loopMergeStrategyTest() {
        String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
        // 将第一列的数据每隔两行进行合并
        LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
        EasyExcel.write(fileName, DownloadDTO.class).registerWriteHandler(loopMergeStrategy)
                .sheet("sheet名称").doWrite(getFruitData());
    }

其中LoopMergeStrategy的源码其实也很简单,底层还是用到了apache poi的API:

    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        if (relativeRowIndex != null) {
            Integer currentColumnIndex;
            if (head != null) {
                currentColumnIndex = head.getColumnIndex();
            } else {
                currentColumnIndex = cell.getColumnIndex();
            }

            // 将入参中指定的列this.columnIndex按照每this.eachRow行进行合并,this.eachRow也是入参
            if (currentColumnIndex == this.columnIndex && relativeRowIndex % this.eachRow == 0) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex() + this.eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex() + this.columnCount - 1);
                sheet.addMergedRegionUnsafe(cellRangeAddress);
            }

        }
    }

然后,在github的issue中,有这样一个使用OnceAbsoluteMergeStrategy的例子:

onceAbsoluteMerge

在这个例子中,我们可以指定坐标范围,对固定区间进行合并,其对应的代码如下:

    @Test
    public void onceMergeStrategyTest() {
        String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
        OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy = new OnceAbsoluteMergeStrategy(1, 3, 1, 1);
        EasyExcel.write(fileName, DownloadDTO.class).registerWriteHandler(onceAbsoluteMergeStrategy)
                .sheet("sheet名称").doWrite(getFruitData());
    }

当然了,我们可以一次使用多个合并策略,比如我们想实现这样的效果:


onceAbsoluteMerge2

OnceAbsoluteMergeStrategy可以这么做:

    @Test
    public void onceMergeStrategyTest() {
        String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
        OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy = new OnceAbsoluteMergeStrategy(1, 3, 1, 1);
        OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy2 = new OnceAbsoluteMergeStrategy(4, 5, 1, 1);
        EasyExcel.write(fileName, DownloadDTO.class)
                .registerWriteHandler(onceAbsoluteMergeStrategy)
                .registerWriteHandler(onceAbsoluteMergeStrategy2)
                .sheet("sheet名称").doWrite(getFruitData());
    }

自定义实现合并策略

当我们需要在写Excel的时候实现更加复杂乃至动态地合并单元格时,就需要自己实现一个合并策略。

比如当我们想实现这样的效果时:


myMerge

在这个例子中,对于第1、4列,我们需要根据记录总数,都合并成一个单元格;对于第2列,我们需要根据每组的记录个数,分别进行单元格的合并;而对于第3列,则不要使用合并策略。

与之对应的代码是:

    @Test
    public void myMergeStrategyTest() {
        String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx";
        MyMergeStrategy myMergeStrategy = new MyMergeStrategy(getFruitData(), getGroupData());
        EasyExcel.write(fileName, DownloadDTO.class).registerWriteHandler(myMergeStrategy)
                .sheet("sheet名称").doWrite(getFruitData());
    }
public class MyMergeStrategy extends AbstractMergeStrategy {

    private List<DownloadDTO> fruitList;
    private List<Integer> fruitGroupCount;
    private Sheet sheet;

    public MyMergeStrategy(List<DownloadDTO> fruitList, List<Integer> fruitGroupCount) {
        this.fruitList = fruitList;
        this.fruitGroupCount = fruitGroupCount;
    }

    // 将该列全部合并成一个单元格
    private void mergeCommonColumn(Integer index) {
        CellRangeAddress cellRangeAddress = new CellRangeAddress(1, fruitList.size(), index, index);
        sheet.addMergedRegionUnsafe(cellRangeAddress);
    }

    // 按照分组将各种类别分别合并成一个单元格
    private void mergeGroupColumn(Integer index) {
        Integer rowCnt = 1;
        for (Integer count : fruitGroupCount) {
            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCnt, rowCnt + count - 1, index, index);
            sheet.addMergedRegionUnsafe(cellRangeAddress);
            rowCnt += count;
        }
    }

    @Override
    protected void merge(org.apache.poi.ss.usermodel.Sheet sheet, Cell cell, Head head, Integer integer) {
        this.sheet = sheet;
        if (cell.getRowIndex() == 1) {
            switch (cell.getColumnIndex()) {
                case 0:
                    this.mergeCommonColumn(0);
                    break;
                case 1:
                    this.mergeGroupColumn(1);
                    break;
                case 2:
                    break;
                case 3:
                    this.mergeCommonColumn(3);
                    break;
                default:
                    break;
            }
        }
    }
}

为什么不使用多个OnceAbsouluteMergeStrategy?

因为我们在写代码的时候并不知道每个分组的数量究竟有多少个,所以只能自己写合适的合并策略了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 转自链接 目录 1.认识NPOI 2.使用NPOI生成xls文件 2.1创建基本内容 2.1.1创建Workboo...
    腿毛裤阅读 10,555评论 1 3
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,864评论 1 22
  • 使用首先需要了解他的工作原理 1.POI结构与常用类 (1)创建Workbook和Sheet (2)创建单元格 (...
    长城ol阅读 8,425评论 2 25
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,101评论 1 32
  • 同样是餐饮行业,为什么“飞鸽小栈”相比起来,特色更加的鲜明,更能让人一眼就记住呢?其实是因为“飞鸽小栈”本身具备优...
    飞鸽小栈阅读 309评论 0 0