apache poi 实现下拉框联动校验

apache poi 提供了 DataValidation​ 接口 让我们可以轻松实现 Excel 下拉框数据局校验。但是下拉框联动校验是无法直接通过 DataValidation ​实现,所以我们可以通过其他方式间接实现。

步骤如下:

  1. 创建一个隐藏 sheet

     private static void createHiddenSheet(List<String> provinceList, Map<String, String[]> regionMap, Workbook workbook) {
            String hiddenSheetName = "region";
            Sheet hiddenSheet = workbook.createSheet(hiddenSheetName);
    
            // 这里也可以设置 hidden 为 false 这样可以直接看到 sheet 内容
            workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);
        }
    
  2. 将数据放入隐藏 sheet

    		int rowNum = 0;
            // 第一行存放省数据
            Row row = hiddenSheet.createRow(rowNum);
            for (int i = 0; i < provinceList.size(); i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(provinceList.get(i));
            }
    
            rowNum++;
            for (String key : regionMap.keySet()) {
                String[] dataArray = regionMap.get(key);
                // 循环创建行,每行存放一个数组
                row = hiddenSheet.createRow(rowNum);
                // key 放在每行第一个,value 放在每行的后面
                Cell keyCell = row.createCell(0);
                keyCell.setCellValue(key);
                for (int i = 0, length = dataArray.length; i < length; i++) {
                    Cell cell = row.createCell(i + 1);
                    cell.setCellValue(dataArray[i]);
                }
                Name name = workbook.createName();
                // 将key 设置为下拉框的key
                name.setNameName(key);
                String formula = hiddenSheetName + "!$B$" + (rowNum + 1) + ":$" + (convertNumberToLetter(dataArray.length + 1)) + "$" + (rowNum + 1);
                name.setRefersToFormula(formula);
    
                // 可以将formula 放在最后一列
                Cell formulaCell = row.createCell(dataArray.length + 1);
                formulaCell.setCellValue(formula);
    
                rowNum++;
            }
    
  3. 在主 sheet 中使用 formula 来使用隐藏 sheet 的数据

        DataValidationHelper helper = mainSheet.getDataValidationHelper();
    
        // 设置省份下拉框
        CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, 1000, 0, 0);
        // formula 为  region!$A$1:$E$1
        DataValidationConstraint dvConstraint = helper.createFormulaListConstraint("region!$A$1:$" + (convertNumberToLetter(provinceList.size())) + "$1");
        DataValidation provinceDataValidation = helper.createValidation(dvConstraint, provRangeAddressList);
    
    
        provinceDataValidation.setSuppressDropDownArrow(true);
        mainSheet.addValidationData(provinceDataValidation);
    
    
  4. 设置联动下拉框 DataValidation

     // 设置市下拉框  firstCol lastCol 根据实际情况设置
     CellRangeAddressList cityRange = new CellRangeAddressList(1, 1000, 1, 1);
     DataValidationConstraint cityConstraint = helper.createFormulaListConstraint("INDIRECT(A2)");
     DataValidation cityValidation = helper.createValidation(cityConstraint, cityRange);
     mainSheet.addValidationData(cityValidation);
    
    
     // 设置县下拉框 firstCol lastCol 根据实际情况设置
     CellRangeAddressList districtRange = new CellRangeAddressList(1, 1000, 2, 2);
     DataValidation districtValidation = helper.createValidation(helper.createFormulaListConstraint("INDIRECT(B2)"), districtRange);
     mainSheet.addValidationData(districtValidation);
    
  5. 完整代码如下:

    package com.shang;
    
    import org.apache.poi.ss.usermodel.*;
    import org.apache.poi.ss.util.CellRangeAddressList;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    
    import java.io.FileOutputStream;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author: shangwei
     * @date: 2024/11/3 13:01
     */
    public class ExcelUtil {
    
        public static void createExcel(String path, List<String> provinceList, Map<String, String[]> regionMap) {
            try {
                Workbook workbook = new XSSFWorkbook();
                createHiddenSheet(provinceList, regionMap, workbook);
    
    
                Sheet mainSheet = workbook.createSheet("mainSheet");
                // 主sheet 第一行数据
                String[] titles = {"省", "市", "县"};
                int rowNum = 0;
                Row row = mainSheet.createRow(rowNum);
                for (int i = 0; i < titles.length; i++) {
                    Cell cell = row.createCell(i);
                    cell.setCellValue(titles[i]);
                }
    
                DataValidationHelper helper = mainSheet.getDataValidationHelper();
    
                // 设置省份下拉框
                CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, 1000, 0, 0);
                // formula 为  region!$A$1:$E$1
                DataValidationConstraint dvConstraint = helper.createFormulaListConstraint("region!$A$1:$" + (convertNumberToLetter(provinceList.size())) + "$1");
                DataValidation provinceDataValidation = helper.createValidation(dvConstraint, provRangeAddressList);
    
                provinceDataValidation.setSuppressDropDownArrow(true);
                mainSheet.addValidationData(provinceDataValidation);
    
                // 设置市下拉框  firstCol lastCol 根据实际情况设置
                CellRangeAddressList cityRange = new CellRangeAddressList(1, 1000, 1, 1);
                DataValidationConstraint cityConstraint = helper.createFormulaListConstraint("INDIRECT(A2)");
                DataValidation cityValidation = helper.createValidation(cityConstraint, cityRange);
                mainSheet.addValidationData(cityValidation);
    
    
                // 设置县下拉框 firstCol lastCol 根据实际情况设置
                CellRangeAddressList districtRange = new CellRangeAddressList(1, 1000, 2, 2);
                DataValidation districtValidation = helper.createValidation(helper.createFormulaListConstraint("INDIRECT(B2)"), districtRange);
                mainSheet.addValidationData(districtValidation);
    
    
                FileOutputStream fileOutputStream = new FileOutputStream(path);
                workbook.write(fileOutputStream);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static void createHiddenSheet(List<String> provinceList, Map<String, String[]> regionMap, Workbook workbook) {
            String hiddenSheetName = "region";
            Sheet hiddenSheet = workbook.createSheet(hiddenSheetName);
            int rowNum = 0;
            // 第一行存放省数据
            Row row = hiddenSheet.createRow(rowNum);
            for (int i = 0; i < provinceList.size(); i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(provinceList.get(i));
            }
    
            rowNum++;
            for (String key : regionMap.keySet()) {
                String[] dataArray = regionMap.get(key);
                // 循环创建行,每行存放一个数组
                row = hiddenSheet.createRow(rowNum);
                // key 放在每行第一个,value 放在每行的后面
                Cell keyCell = row.createCell(0);
                keyCell.setCellValue(key);
                for (int i = 0, length = dataArray.length; i < length; i++) {
                    Cell cell = row.createCell(i + 1);
                    cell.setCellValue(dataArray[i]);
                }
                Name name = workbook.createName();
                // 将key 设置为下拉框的key
                name.setNameName(key);
                String formula = hiddenSheetName + "!$B$" + (rowNum + 1) + ":$" + (convertNumberToLetter(dataArray.length + 1)) + "$" + (rowNum + 1);
                name.setRefersToFormula(formula);
    
                // 可以将formula 放在最后一列
                Cell formulaCell = row.createCell(dataArray.length + 1);
                formulaCell.setCellValue(formula);
    
                rowNum++;
            }
            // 这里也可以设置 hidden 为 false 这样可以直接看到 sheet 内容
            workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);
        }
    
        /**
         * 将数字 1 到 26 转换为对应的字母 A 到 Z。
         *
         * @param number 要转换的数字,范围是 1 到 26。
         * @return 对应的字母。
         */
        public static String convertNumberToLetter(int number) {
            if (number < 1 || number > 26) {
                throw new IllegalArgumentException("Number must be between 1 and 26");
            }
            return String.valueOf((char) ('A' + number - 1));
        }
    
    
        public static void main(String[] args) {
            Map<String, String[]> regionMap = new HashMap<>();
            List<String> provinceList = Arrays.asList("湖北省", "湖南省", "广东省", "江苏省", "浙江省");
    
            regionMap.put("湖北省", new String[]{"武汉市", "黄石市", "十堰市", "宜昌市", "襄樊市", "鄂州市", "荆门市", "孝感市", "荆州市", "黄冈市", "咸宁市", "随州市"});
            regionMap.put("湖南省", new String[]{"长沙市", "株洲市", "湘潭市", "衡阳市", "邵阳市", "岳阳市", "常德市", "张家界市", "益阳市", "郴州市", "永州市", "怀化市"});
            regionMap.put("广东省", new String[]{"广州市", "韶关市", "深圳市", "珠海市", "汕头市", "佛山市", "江门市", "湛江市", "茂名市", "肇庆市", "惠州市", "梅州市", "汕尾市", "河源市", "阳江市", "清远市"});
            regionMap.put("江苏省", new String[]{"南京市", "无锡市", "徐州市", "常州市", "苏州市", "南通市", "连云港市", "淮安市", "盐城市", "扬州市", "镇江市", "泰州市", "宿迁市"});
            regionMap.put("浙江省", new String[]{"杭州市", "宁波市", "温州市", "嘉兴市", "湖州市", "绍兴市", "金华市", "衢州市", "舟山市", "台州市", "丽水市"});
            regionMap.put("武汉市", new String[]{"江岸镇", "江汉镇", "江夏镇", "硚口镇", "武昌镇", "江夏镇"});
            regionMap.put("黄石市", new String[]{"黄石港镇", "西塞山镇", "下陆镇", "大冶镇", "大冶镇"});
    
            String path = "/Users/shangwei/Desktop/example" + System.currentTimeMillis() + ".xlsx";
            createExcel(path, provinceList, regionMap);
    
        }
    
    
    }
    
    

相关 Maven 依赖


        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>

运行截图:

image

  • POI
    23 引用 • 21 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 75 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    142 引用 • 442 回帖 • 1 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 778 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 2 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 1 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 76 关注
  • Angular

    AngularAngularJS 的新版本。

    26 引用 • 66 回帖 • 535 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 53 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    36 引用 • 37 回帖 • 536 关注
  • 反馈

    Communication channel for makers and users.

    123 引用 • 913 回帖 • 249 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 483 关注
  • 阿里云

    阿里云是阿里巴巴集团旗下公司,是全球领先的云计算及人工智能科技公司。提供云服务器、云数据库、云安全等云计算服务,以及大数据、人工智能服务、精准定制基于场景的行业解决方案。

    89 引用 • 345 回帖 • 1 关注
  • 七牛云

    七牛云是国内领先的企业级公有云服务商,致力于打造以数据为核心的场景化 PaaS 服务。围绕富媒体场景,七牛先后推出了对象存储,融合 CDN 加速,数据通用处理,内容反垃圾服务,以及直播云服务等。

    27 引用 • 225 回帖 • 162 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 17 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 486 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 3 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    728 引用 • 1275 回帖
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    355 引用 • 1824 回帖 • 1 关注
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    6 引用 • 63 回帖 • 4 关注
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 336 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    408 引用 • 3578 回帖
  • 职场

    找到自己的位置,萌新烦恼少。

    127 引用 • 1706 回帖
  • 安全

    安全永远都不是一个小问题。

    200 引用 • 816 回帖 • 1 关注