文章目录

  • 一、项目需求
  • 二、解决方案
  • 三、适用场景
  • 四、使用方法
    • 1、导入jar包
    • 2、导入工具类
    • 3、测试代码
    • 4、效果图

一、项目需求

现有产品优化,将原有的Excel三级联动功能升级为四级联动,考虑到未来产品可能会有更多层级的联动功能,故抽取出来成为一个通用方法。

二、解决方案

使用POI技术,将需要联动的数据写入Excel中。

三、适用场景

对任意的Excel文件,实现在指定行、指定列插入N级联动。

四、使用方法

1、导入jar包

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

2、导入工具类

Java代码中导入ExcelUtils和CharStatusEnum两个类,CharStatusEnum为字符枚举类,对应匹配ExcelUtils中入参的起始列。

package com.excel.utils;import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFDataValidationConstraint;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
import org.apache.poi.xssf.usermodel.XSSFSheet;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;public class ExcelUtils {private static final Integer ENDROW = 3000;/*** @param filePath   文件路径* @param mapOneList 一级数据* @param map        N级数据(需递归获取每层数据,统一封装为map)* @param startRow   开始插入行* @param startCol   开始插入列* @param levels     N级联动*/public static void export(String filePath, List<String> mapOneList, Map<String, List<String>> map, Integer startRow, Integer startCol, Integer levels) throws Exception {FileInputStream fis = new FileInputStream(filePath);// 1、创建ExcelWorkbook workbook = WorkbookFactory.create(fis);Sheet mainSheet = workbook.getSheetAt(0);//2、N级联动 sheetSheet mapSheet = workbook.createSheet("SHEET_MAP");//联动关系写入mapSheetworkbook.setSheetHidden(workbook.getSheetIndex(mapSheet), true);// 设置sheet是否隐藏// 3、将数据写入隐藏的sheet中并做好关联关系writeData(workbook, mapSheet, mapOneList, map);//// 4、设置数据有效性----CharStatusEnum.A从第几列开始插入,A从第1列,B第二列for (CharStatusEnum charStatusEnum : CharStatusEnum.values()) {if (charStatusEnum.getValue() == startCol) {setDataValid(workbook, mainSheet, mapOneList, charStatusEnum, startRow, levels);}}FileOutputStream os = null;try {String exisname = filePath.substring(0, filePath.lastIndexOf("\\"));File f = new File(exisname);if (!f.exists()) {f.mkdirs();}// 创建可写入的Excel工作簿File file = new File(filePath);if (!file.exists()) {boolean bool = file.createNewFile();System.out.println(bool);} else {file.delete();file.createNewFile();}os = new FileOutputStream(filePath);workbook.write(os);} catch (Exception e) {e.printStackTrace();} finally {IOUtils.closeQuietly(os);}}private static void writeData(Workbook hssfWorkBook, Sheet mapSheet, List<String> provinceList, Map<String, List<String>> siteMap) {//循环将父数据写入siteSheet的第1行中int siteRowId = 0;Row provinceRow = mapSheet.createRow(siteRowId);provinceRow.createCell(0).setCellValue("父列表");for (int i = 0; i < provinceList.size(); i++) {//有多少个省,创建多少个下拉框provinceRow.createCell(i + 1).setCellValue(provinceList.get(i));}// 将具体的数据写入到每一行中,行开头为父级区域,后面是子区域。Iterator<String> keyIterator = siteMap.keySet().iterator();while (keyIterator.hasNext()) {String key = keyIterator.next();List<String> son = siteMap.get(key);Row siteRow = mapSheet.createRow(siteRowId++);siteRow.createCell(0).setCellValue(key);for (int i = 0; i < son.size(); i++) {siteRow.createCell(i + 1).setCellValue(son.get(i));}// 添加名称管理器: 偏移量、第几行、一共多少列String range = getRange(1, siteRowId, son.size());Name name = hssfWorkBook.createName();name.setNameName(key);String formula = mapSheet.getSheetName() + "!" + range;name.setRefersToFormula(formula);}}/*** 计算formula** @param offset   偏移量,如果给0,表示从A列开始,1,就是从B列* @param rowId    第几行* @param colCount 一共多少列* @return 如果给入参 1,1,10. 表示从B1-K1。最终返回 $B$1:$K$1*/private static String getRange(int offset, int rowId, int colCount) {char start = (char) ('A' + offset);if (colCount <= 25) {char end = (char) (start + colCount - 1);return "$" + start + "$" + rowId + ":$" + end + "$" + rowId;} else {char endPrefix = 'A';char endSuffix = 'A';if ((colCount - 25) / 26 == 0 || colCount == 51) {// 26-51之间,包括边界(仅两次字母表计算)if ((colCount - 25) % 26 == 0) {// 边界值endSuffix = (char) ('A' + 25);} else {endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);}} else {// 51以上if ((colCount - 25) % 26 == 0) {endSuffix = (char) ('A' + 25);endPrefix = (char) (endPrefix + (colCount - 25) / 26 - 1);} else {endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);endPrefix = (char) (endPrefix + (colCount - 25) / 26);}}return "$" + start + "$" + rowId + ":$" + endPrefix + endSuffix + "$" + rowId;}}private static void setDataValid(Workbook workbook, Sheet mainSheet, List<String> provinceList, CharStatusEnum charEnum, Integer startRow, Integer levels) {//设置省份下拉DataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet) mainSheet);String[] dataArray = provinceList.toArray(new String[0]);Sheet hidden = workbook.createSheet("hidden");Cell cell = null;for (int i = 0, length = dataArray.length; i < length; i++) {String name = dataArray[i];Row row = hidden.createRow(i);cell = row.createCell(0);cell.setCellValue(name);}Name namedCell = workbook.createName();namedCell.setNameName("hidden");namedCell.setRefersToFormula("hidden!$A$1:$A$" + dataArray.length);//加载数据,将名称为hidden的XSSFDataValidationConstraint constraint = new XSSFDataValidationConstraint(DataValidationConstraint.ValidationType.LIST, "hidden");// 四个参数分别是:起始行、终止行、起始列、终止列.CellRangeAddressList provinceRangeAddressList = new CellRangeAddressList(startRow - 1, ENDROW, charEnum.getValue() - 1, charEnum.getValue() - 1);DataValidation provinceDataValidation = dvHelper.createValidation(constraint, provinceRangeAddressList);provinceDataValidation.createErrorBox("error", "请选择正确");provinceDataValidation.setShowErrorBox(true);// 设置sheet是否隐藏workbook.setSheetHidden(workbook.getSheetIndex(hidden), true);mainSheet.addValidationData(provinceDataValidation);// 设置非第一层级的数据下拉for (int i = 0; i <= 10; i++) {// setDataValidation('B', mainSheet, i + 1, 2);// "B"是指父类所在的列,i+1初始值为1代表从第2行开始// 2要与“B”对应,为B的列号加1,假如第一个参数为“C”,那么对应的第四个参数就为3setDataValidation(charEnum.getCharacter(), mainSheet, i + 1, charEnum.getValue(), levels);}}/*** 设置有效性** @param offset 主影响单元格所在列,即此单元格由哪个单元格影响联动* @param sheet* @param rowNum 行数* @param colNum 列数*/private static void setDataValidation(char offset, Sheet sheet, int rowNum, int colNum, Integer levels) {DataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet) sheet);for (Integer i = 0; i < levels - 1; i++) {sheet.addValidationData(getDataValidationByFormula("INDIRECT($" + (char) (offset + i) + (rowNum) + ")", rowNum - 1, colNum + i, dvHelper));}}private static DataValidation getDataValidationByFormula(String formulaString, int naturalRowIndex, int naturalColumnIndex, DataValidationHelper dvHelper) {DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint(formulaString);CellRangeAddressList regions = new CellRangeAddressList(naturalRowIndex, 65535, naturalColumnIndex, naturalColumnIndex);DataValidation data_validation_list = dvHelper.createValidation(dvConstraint, regions);data_validation_list.setEmptyCellAllowed(false);if (data_validation_list instanceof XSSFDataValidationHelper) {data_validation_list.setShowErrorBox(true);} else {//  data_validation_list.setSuppressDropDownArrow(false);}// 设置输入信息提示信息data_validation_list.createPromptBox("下拉选择提示", "请使用下拉方式选择合适的值!");return data_validation_list;}}
package com.excel.utils;import java.util.*;/*** 三级联动指定插入位置,eg: B为指定第二列插入,C为指定第三列插入*/
public enum CharStatusEnum {A('A',1), B('B',2), C('C',3),D('D',4),E('E',5),F('F',6),G('G',7),H('H',8),I('I',9),J('J',10);private char character;private int value;//构造方法CharStatusEnum(char character, int value) {this.character=character;this.value=value;}//自定义方法public void setCharacter(char character){this.character=character;}public void setValue(int value){this.value=value;}public char getCharacter() {return character;}public int getValue() {return value;}@Overridepublic String toString() {return "CharStatusEnum{" +"character=" + character +", value=" + value +'}';}
}

3、测试代码

public static void main(String[] args) throws Exception {String filePath = "C:\\Users\\白杉\\Desktop\\测试级联.xlsx";//所有一级List<String> mapOneList = new ArrayList<String>();mapOneList.add("广东省");mapOneList.add("湖北省");mapOneList.add("安徽省");//一二级关系mapMap<String, List<String>> map = new HashMap<String, List<String>>();map.put("广东省", Arrays.asList("广州市", "佛山市"));map.put("湖北省", Arrays.asList("武汉市", "荆州市"));map.put("广州市", Arrays.asList("白云区", "越秀区"));map.put("佛山市", Arrays.asList("顺德区", "南海区"));map.put("安徽省", Arrays.asList("合肥市", "芜湖市"));map.put("合肥市", Arrays.asList("肥东县", "肥西县"));map.put("肥东县", Arrays.asList("西山镇","店埠"));map.put("西山镇", Arrays.asList("西山镇123","店埠123"));//第2行第3列插入5级联动ExcelUtilsFinal11.export(filePath,mapOneList,map,2,3,5);}

4、效果图

导入前:

导入后:(数据节点有几级,该层级则联动到几级)

java实现excel多级联动相关推荐

  1. 关于使用JavaPOI 导出Excel多级联动的一些方法

    关于使用JavaPOI 导出Excel多级联动 0.记录原因 最近有个需求是需要导出Excel模板,里面有涉及到某些字段只能做下拉选择,有单个下拉也有多级联动的.写了好久,在网上也查了很多资料,但是感 ...

  2. java 下拉多级联动 头像裁剪 调用摄像头 SpringMVC mybatis SSM

    获取[下载地址]   QQ 313596790 三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体 [新录针对本系统的视频教程,手把手教开发一个模块,快速 ...

  3. Java Excel 多级菜单联动原理与实现(可扩展)

    Java Excel级联菜单实现(可扩展) 为什么要写这篇文章呢,因为看到了有人在提问如何用Java做Excel的级联菜单效果.帖子详情:http://spring4all.com/forum-pos ...

  4. excel图表交互联动_如何使用高大上的多级联动交互式图表来分析人员结构?

    关注[新精英充电站]能力提升看得见! 众所周知,Excel中的图表是分析数据最重要一枚利器.但一般我们分析数据时使用的图表都是静态的,一个图表只能展示一个数据维度的情况,如果需要展示的数据有多个维度, ...

  5. Excel·VBA多级联动的数据有效性

    Function val_lv(arr, Optional lv& = 1)'数据有效性级别函数,arr为数据有效性的数组,lv为级别,返回第lv级的规则数据:arr建议从表格读取'第lv级的 ...

  6. php excel多级下拉菜单自动匹配,Excel下拉菜单怎么做 多级联动+自动匹配教程

    Excel一直是近年来办公室工作中的必要软件之一,这个软件功能非常强大,如果你只学会了皮毛那就有些可惜了,而Excel隐藏了许多许多的小技巧.今天UU为大家带来的是Excel下拉菜单怎么做,其中包括多 ...

  7. WPS Excel多级下拉菜单联动去除子集中的空值

    WPS Excel多级下拉菜单联动去除子集中的空值 实现效果: 在图中可以看出四个省下面的城市数量并不相等.若是按照正常操作下来的结果如下图: 我们可以看到虽然城市不为空,但是会有空选项,这不是我们要 ...

  8. Excel制作导入模板,多级联动下拉框(一整列的设置),修改一级下拉框内容,自动清空二级内容

    目录 效果展示 一.数据准备 二.模板制作 原因1:为空 原因二:名称管理器数据有误 三.修改一级下拉框内容,自动清空二级内容 1,效果演示 2,实际操作 效果展示 一.数据准备 1,新建一张表,在s ...

  9. java 导出excel 列头,java 生成excel (多级表头)导出

    依赖工具包 cn.hutool hutool-all 5.3.5 该工具包是对 Apache POI包的分装 private void export(HttpServletResponse respo ...

最新文章

  1. 第十六届智能车竞赛比赛进行中 - 参赛队员提出的问题
  2. VTK:IO之ReadOBJ
  3. spring session实现集群中session共享
  4. 关于页面之间传参时有空格,中文及点击页面后退按钮的问题
  5. Python set list dict tuple 区别和相互转换 - Python零基础入门教程
  6. vue实现数字“滚动式增加”效果 【插件化封装】
  7. 大数据学习笔记40:Hive - 内置函数(3)
  8. 运用Unity实现AOP拦截器[结合异常记录实例]
  9. 2017级C语言大作业 - 奇怪的大冒险
  10. Linux 命令之 tar 备份与解压缩
  11. 如何给你的Android 安装文件(APK)瘦身
  12. 【高德地图API】如何打造十月妈咪品牌地图?
  13. PSS E v33.40 1CD(大型电力系统仿真计算软件)
  14. 用户使用DocConvert处理文档后对DocConvert的评价
  15. Deep Reinforcement Learning : An Overview(Yuxi Li) 学习笔记
  16. 《1818黄金眼》将与bilibili合作:两大快乐源泉要合体了
  17. Zynga公布2021年第二季度财务业绩
  18. bat 自定义位数随机密码生成器
  19. 自动摘要生成(一):最大边界相关算法(MMR)
  20. 小程序开发API之监听加速计wx.startAccelerometer

热门文章

  1. 网页设计 尺寸 html5,网页设计一般至少设置多少高度?制作网站时网页的尺寸是多少?网页的屏幕尺寸是多少?...
  2. whistle-安卓手机配置代理
  3. 【英语】为什么老外说的我们听不懂?
  4. 图形轨迹c语言,OpenCV识别图像上的线条轨迹
  5. win2012故障转移mysql集群_Windows 2012 系统搭建高可用故障转移集群
  6. 全文检索语句中的AND和OR的用法
  7. 基于 Node.js + Koa 构建完整的 Web API (配置 ESLint 和使用 Airbnb 编码规范)
  8. 目标检测Tensorflow:Yolo v3代码详解 (2)
  9. L2TP/IPSec 服务端安装
  10. 历届蓝桥杯Scratch编程国赛 初级 中级 青少年编程比赛国赛真题解析【持续更新 已更新至27题】