关于使用JavaPOI 导出Excel多级联动

0.记录原因

最近有个需求是需要导出Excel模板,里面有涉及到某些字段只能做下拉选择,有单个下拉也有多级联动的。写了好久,在网上也查了很多资料,但是感觉不是很全面,所以走了很多弯路。由此,我整理了《关于使用JavaPOI导出Excel多级联动》的这份文档,和大家分享。

1.POI中的HSSF和XSSF的介绍

关于POI的介绍我就不多说了,大家查下API(http://poi.apache.org/apidocs/dev/org/apache/poi ),我就只说一下HSSF和XSSF:
HSSF主要是支持Excel2007版本之前的类,XSSF主要是支持Excel2007版本之后的类,自己感觉区别不大,但是在使用的时候会有一些细微的差别,后面会说到。

2.中间具体的一些方法的介绍

注:因为考虑到可能存在超过Excel数据规则长度的问题,所以我都采用的是引用sheet数据的方法。

  1. 创建WorkBook
    XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
  2. 创建Sheet页
    XSSFSheet xssfSheet = xssfWorkbook.createSheet(sheetName);
    xssfWorkbook.setSheetHidden(xssfWorkbook.getSheetIndex(sheetName), true);
    //sheetName为String类型,就是一个名字。
    //setSheetHidden的作用是设置sheet是否隐藏。 true:隐藏,false:显示
  3. 创建Row
    XSSFRow xssfRow = xssfSheet.createRow(0);
    //0代表创建第一行,Excel中行计数和列计数都是从0开始。
  4. 创建当前行的列
    XSSFCell xssfCell = xssfRow .createCell(0);
    //0代表创建第一列即第A列
    xssfCell .setCellValue();
    //为当前行的当前列设置值
  5. 设置数据规则(是应该这么说?)
    Name name = xssfWorkbook .createName();
    name .setNameName(“xx”);//定义一个名字
    name .setRefersToFormula(sheetName+ " !$A1:1:1:A"+参数(int类型));//setRefersToFormula这个方法的作用是将我们定义的这个名字跟我指定的数据进行对应,例如sheetName!"+ 参数(int类型)); //setRefersToFormula这个方法的作用是将我们定义的这个名字跟我指定的数据进行对应,例如 sheetName!"+参数(int类型));//setRefersToFormula这个方法的作用是将我们定义的这个名字跟我指定的数据进行对应,例如sheetName!A1:1:1:A$10,就是将xx与sheetName这个页签中的第A1到A10行的数据进行绑定,供后期使用。
  6. 设置数据有效性(此处可以简单理解为设置下拉框)
    sheetName.addValidationData(DataValidation dataValidation);
    //注意!!!这里要主要sheetName的类型是XSSF还是HSSF,类型不一样,传进去的DataValidation也是不一样的!!!

3.实例分享

以性别做单个下拉实例,已省市区做多级联动下拉实例;

import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFDataValidationHelper;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationConstraint.OperatorType;
import org.apache.poi.ss.usermodel.DataValidationConstraint.ValidationType;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;import com.google.common.collect.Lists;public class ExcelExport {public static void main(String[] args) {export();}public static void export() {
// 1.准备数据// 1)单个下拉数据List<String> genderList = new ArrayList<String>();genderList.add("male");genderList.add("female");// 2)多级联动下拉数据List<String> provinceList = new ArrayList<String>();provinceList.add("广东省");provinceList.add("湖北省");Map<String, List<String>> siteMap = new HashMap<String, List<String>>();siteMap.put("广东省", Lists.newArrayList("广州市", "佛山市"));siteMap.put("湖北省", Lists.newArrayList("武汉市", "荆州市"));siteMap.put("广州市", Lists.newArrayList("白云区", "越秀区"));siteMap.put("佛山市", Lists.newArrayList("顺德区", "南海区"));
// 2.创建Excel// 1)创建workbookHSSFWorkbook hssfWorkBook = new HSSFWorkbook();// 2)创建sheetHSSFSheet mainSheet = hssfWorkBook.createSheet("mainSheet");// 主sheet// 用于展示//2.1 创建表头,供用户输入HSSFRow headRow = mainSheet.createRow(0);// 创建第一行headRow.createCell(0).setCellValue("gender");headRow.createCell(1).setCellValue("province");headRow.createCell(2).setCellValue("city");headRow.createCell(3).setCellValue("area");headRow.createCell(4).setCellValue("date");headRow.createCell(5).setCellValue("num1");headRow.createCell(6).setCellValue("num2");HSSFSheet genderSheet = hssfWorkBook.createSheet("genderSheet");// 隐藏sheet// 用于隐藏性别数据HSSFSheet siteSheet = hssfWorkBook.createSheet("siteSheet");// 隐藏sheet// 用于隐藏地点数据hssfWorkBook.setSheetHidden(hssfWorkBook.getSheetIndex(genderSheet), false);// 设置sheet是否隐藏// true:隐藏/false:显示hssfWorkBook.setSheetHidden(hssfWorkBook.getSheetIndex(siteSheet), false);// 设置sheet是否隐藏// true:隐藏/false:显示
// 3.写入数据writeData(hssfWorkBook, genderSheet, siteSheet, genderList, provinceList, siteMap);// 将数据写入隐藏的sheet中并做好关联关系
// 4.设置数据有效性setDataValid(hssfWorkBook, mainSheet, genderList, provinceList, siteMap);// 5.设置时间规则setDateFormat(hssfWorkBook, mainSheet);// 6.设置数据规则List<Integer> cellNumList = new ArrayList<Integer>();cellNumList.add(5);cellNumList.add(6);setNumberFormat(hssfWorkBook, mainSheet, cellNumList);FileOutputStream os = null;try {os = new FileOutputStream("D:/excelExport.xls");hssfWorkBook.write(os);} catch (Exception e) {e.printStackTrace();} finally {IOUtils.closeQuietly(os);}}//public static void setDataValid(HSSFWorkbook HSSFWorkBook, HSSFSheet mainSheet, List<String> genderList, List<String> provinceList, Map<String, List<String>> siteMap) {//设置省份下拉HSSFDataValidationHelper dvHelper = new HSSFDataValidationHelper((HSSFSheet) mainSheet);DataValidationConstraint provinceConstraint = dvHelper.createExplicitListConstraint(provinceList.toArray(new String[] {}));CellRangeAddressList provinceRangeAddressList = new CellRangeAddressList(1, 60, 1, 1);//意思是从B2:B61 为下拉DataValidation provinceDataValidation = dvHelper.createValidation(provinceConstraint, provinceRangeAddressList);provinceDataValidation.createErrorBox("error", "请选择正确的省份");provinceDataValidation.setShowErrorBox(true);// provinceDataValidation.setSuppressDropDownArrow(true);mainSheet.addValidationData(provinceDataValidation);//设置性别下拉DataValidationConstraint genderConstraint = dvHelper.createFormulaListConstraint("gender");CellRangeAddressList genderRangeAddressList = new CellRangeAddressList(1, 60, 0, 0);//意思是从A2:A61 为下拉HSSFDataValidation genderDataValidation = (HSSFDataValidation) dvHelper.createValidation(genderConstraint, genderRangeAddressList);genderDataValidation.createErrorBox("Error", "请选择或输入有效的选项,或下载最新模版重试!");// genderDataValidation.setSuppressDropDownArrow(true);mainSheet.addValidationData(genderDataValidation);// 设置市、区下拉for (int i = 0; i <= 60; i++) {setDataValidation('B', mainSheet, i + 1, 2);// "B"是指省所在的列,i+1初始值为1代表从第2行开始,2要与“B”对应,为B的列号加1,假如第一个参数为“C”,那么最后一个参数就3}}public static void setDataValidation(char offset, HSSFSheet sheet, int rowNum, int colNum) {HSSFDataValidationHelper dvHelper = new HSSFDataValidationHelper(sheet);DataValidation dataValidationList1;DataValidation dataValidationList2;dataValidationList1 = getDataValidationByFormula("INDIRECT($" + offset + (rowNum) + ")", rowNum, colNum, dvHelper);dataValidationList2 = getDataValidationByFormula("INDIRECT($" + (char) (offset + 1) + (rowNum) + ")", rowNum, colNum + 1, dvHelper);sheet.addValidationData(dataValidationList1);sheet.addValidationData(dataValidationList2);}private static DataValidation getDataValidationByFormula(String formulaString, int naturalRowIndex, int naturalColumnIndex, HSSFDataValidationHelper dvHelper) {DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint(formulaString);CellRangeAddressList regions = new CellRangeAddressList(naturalRowIndex, 65535, naturalColumnIndex, naturalColumnIndex);HSSFDataValidation data_validation_list = (HSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);data_validation_list.setEmptyCellAllowed(false);if (data_validation_list instanceof HSSFDataValidation) {// data_validation_list.setSuppressDropDownArrow(true);data_validation_list.setShowErrorBox(true);} else {// data_validation_list.setSuppressDropDownArrow(false);}// 设置输入信息提示信息data_validation_list.createPromptBox("下拉选择提示", "请使用下拉方式选择合适的值!");return data_validation_list;}public static void writeData(HSSFWorkbook hssfWorkBook, HSSFSheet genderSheet, HSSFSheet siteSheet, List<String> genderList, List<String> provinceList, Map<String, List<String>> siteMap) {//循环将性别的数据写入genderSheet的第A列中for (int i = 0; i < genderList.size(); i++) {HSSFRow genderRow = genderSheet.createRow(i);genderRow.createCell(0).setCellValue(genderList.get(i));}initGenderMapping(hssfWorkBook, genderSheet.getSheetName(), genderList.size());// 创建性别数据规则//循环将省数据写入siteSheet的第1行中int siteRowId = 0;HSSFRow provinceRow = siteSheet.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);HSSFRow siteRow = siteSheet.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 = siteSheet.getSheetName() + "!" + range;name.setRefersToFormula(formula);}}// 创建性别数据规则private static void initGenderMapping(HSSFWorkbook workbook, String genderSheetName, int genderQuantity) {Name genderName = workbook.createName();genderName.setNameName("gender");genderName.setRefersToFormula(genderSheetName + "!$A$1:$A$" + genderQuantity);}public 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;}}// TODO 测试时间格式public static void setDateFormat(HSSFWorkbook HSSFWorkbook, HSSFSheet mainsheet) {HSSFDataValidationHelper dvHelper = new HSSFDataValidationHelper(mainsheet);CellRangeAddressList regions = new CellRangeAddressList(1, 60, 4, 4);DataValidationConstraint dvConstraint = dvHelper.createDateConstraint(OperatorType.BETWEEN, "1900-01-01", "5000-12-31", "yyyy-MM-dd");HSSFDataValidation dataValidation = (HSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);// dataValidation.setSuppressDropDownArrow(false);dataValidation.createPromptBox("输入提示", "请填写日期格式'yyyy-mm-dd'");dataValidation.setShowPromptBox(true);dataValidation.createErrorBox("日期格式错误提示", "你输入的日期格式不符合'yyyy-mm-dd'格式规范,请重新输入!");dataValidation.setShowErrorBox(true);mainsheet.addValidationData(dataValidation);}// TODO 测试数字规则public static void setNumberFormat(HSSFWorkbook HSSFWorkbook, HSSFSheet mainsheet, List<Integer> colNum) {if (colNum.size() > 0) {for (Integer index : colNum) {HSSFDataValidationHelper dvHelper = new HSSFDataValidationHelper(mainsheet);CellRangeAddressList regions = new CellRangeAddressList(1, 60, index, index);DataValidationConstraint dvConstraint = dvHelper.createNumericConstraint(ValidationType.DECIMAL, OperatorType.BETWEEN, "0.0001", "100000");HSSFDataValidation dataValidation = (HSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);// dataValidation.setSuppressDropDownArrow(false);dataValidation.createPromptBox("", "请填写数字(至多4位小数)!");dataValidation.setShowPromptBox(true);dataValidation.createErrorBox("数字格式错误提示", "你输入的数字不正确,请重新输入!");dataValidation.setShowErrorBox(true);mainsheet.addValidationData(dataValidation);}}}
}

4.后记

实例分享中,实际上“性别”的下拉和 “省”的都是属于单个下拉,大家仔细看,这两个下拉框的数据其实插入的方式是不同的,“性别”是采用引用sheet页数据的方式,而“省份”是采用直接填充数据的方式。值得注意的是,像“省份”这种直接填充数据的方式是有限制的,因为Excel中填充数据的地方是有长度限制(具体可点击Excel中的 数据->数据验证->数据验证 查看),如果这个下拉框的数据量比较大,那么很有可能会超出长度,但是在导出的时候并不会报错,直到打开文件的时候会提示“文件有部分错误是否需要修复”,选择否,文件不会打开,选择是,会将所有的数据规则及有效性全部清除,很坑爹,笔者在这里被坑过。所以切记,当数据量比较大的时候,一定要采用引用sheet页数据的方式。

5.感谢

文中的部分代码为引用,在此特别鸣谢,若需授权才能使用,请联系我删除
感谢:
@hzhqk(文章地址:https://www.jianshu.com/p/be2f1af3af92)
@水草镜(文章地址:https://blog.csdn.net/m0_37956938/article/details/78084503)

6.最后

没了 2019-04-19 19:42 LianYungang, JiangSu, China

7.修改补充

2019-04-20 16:35 LianYungang, JiangSu, China
在昨天写出了联动的下拉框之后,笔者又想到了设置验证数据规则,比如时间格式,数据类型等等,在写数字验证的时候很容易 没发现什么问题,但是在写时间格式数据规则的时候,因为之前使用的是XSSF类型的,所以使用了XSSFDataValidationHelper这个类,但总是会写出错 如数据规则应该是“2019-04-20”,用这个类里面的createDateConstraint方法写出来之后就变成了“=2019-04-20”,以为自己写的有什么问题就去百度了一下,发现大家都在报这个bug。。。没办法,我只有把我所有XSSF换成了HSSF,最后实现了需要的东西,所以没有对Excel版本有特殊要求的话,用POI导出的时候尽量使用HSSF来操作。以上的代码已经更新,感兴趣的朋友可以copy看看。

关于使用JavaPOI 导出Excel多级联动的一些方法相关推荐

  1. .net 导出excel_C# 导出 Excel 的 6 种简单方法!你会几种?

    [CSDN 编者按]C# 导出 Excel 的6种简单方法:数据表导出到 Excel,对象集合导出到 Excel,数据库导出到 Excel,微软网格控件导出到 Excel,数组导出到 Excel,CS ...

  2. SAS导出EXCEL文件的几种方法

    SAS学习 SAS导出EXCEL文件的几种方法 以下基于SAS 9.3简体中文版 一.proc export proc export data=a.dataname outfile='D:\excel ...

  3. java实现excel多级联动

    文章目录 一.项目需求 二.解决方案 三.适用场景 四.使用方法 1.导入jar包 2.导入工具类 3.测试代码 4.效果图 一.项目需求 现有产品优化,将原有的Excel三级联动功能升级为四级联动, ...

  4. javaPOI导出excel代码

    此文件已经通过测试 OK, 需要poi-3.7-20101029.jar 等日志Jar包 import java.io.File; import java.io.FileInputStream; im ...

  5. DataSet导出Excel,比以往的方法导出的Excel外观更加好看

    原文发布时间为:2010-06-21 -- 来源于本人的百度文章 [由搬家工具导入] ======目前方法=========== #region 生成Excel /// <summary> ...

  6. .NET CORE 2.1 导出excel文件的两种方法

    最近在做 MVC 项目的时候遇到项目的导出,下面总结下两种导出到excel 的方法 第一种方法: 将文件写到本地,然后返回这个File 或者返回这个 File 的绝对地址  其中  _hostingE ...

  7. asp.net中GridView多行表头导出Excel表头显示不全问题解决方法

    本次需要导出的Excel表格格式如下: 项目 本周实际 本月实际 本年实际 刷卡额 佣金收入 刷卡额 佣金收入 刷卡额 佣金收入 多表头具体的后台代码是在Row_Created事件中创建的.先看创建代 ...

  8. node.js/VUE项目中导出excel表格的多种实现方法(D2admin适用)

    方法一.vue+js-xlsx 1.vue项目内安装两个依赖:xlsx.file-saver  npm install xlsx --save  npm install file-saver --sa ...

  9. Vue用Export2Excel导出excel,多级表头数据

    VUE 使用Export2Excel导出数据,以及多级表头 最近公司做项目,需要导出数据到excel表格 使用到了 element UI ,Export2Excel 特此记录,毕竟好记性不如烂笔头 不 ...

最新文章

  1. anndroid ndk使用
  2. html5贝塞尔,使用HTML5画布绘制贝塞尔曲线
  3. C++学习之路 | PTA乙级—— 1093 字符串A+B (20 分)(精简)
  4. simulink中文_CarSimamp;Simulink 联合仿真案例
  5. Python 爬虫没有思路?可以看看这篇文章
  6. python实现带头结点的单链表的就地逆置_6-1 带头结点的单链表就地逆置 (10 分)...
  7. springmvc 优点_深入整合SSM框架引发底层原理——SpringMVC
  8. LeetCode--026--删除排序数组中的重复项(java)
  9. VS2010 + VS2008 快捷键
  10. 190710每日一句
  11. 一元线性回归 R语言
  12. 通过IMAP方式迁移U-Mail邮件到Exchange 2013之Exchange 2007 升级到Exchange 2013!
  13. 数学问题1 - 两个圆圈,小圆贴着大圆外部转过一圈,问小圆转几圈
  14. java常用混淆工具(有链接)
  15. 游戏服务器——中心服
  16. 3D-Max 软件许可证检出失败 错误20 解决办法
  17. 顺丰下单后处理接收到的xml
  18. ##报错:DrawerLayout must be measured with MeasureSpec.EXACTLY. - 在使用ToolBar+DrawerLayout可能会报这个错误 - 1,
  19. 【c#】键盘事件(keypress keydown keyup)
  20. RTU-518G 研究

热门文章

  1. python异步读写文件_一文彻底搞懂python文件读写
  2. 向量和矩阵梯度:标量Hesse矩阵和矢量Jacobian矩阵
  3. 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式
  4. 计算机设备维护保养和网络巡检,弱电设备的维护保养及巡检管理制度
  5. 2023美赛思路 | 2023美赛C题Matlab代码
  6. Windows XP实用技巧44条
  7. Google hack 语法
  8. android 读取manifestPlaceholders配置的值
  9. onscroll事件没有响应的问题
  10. python绘制k线图_Python使用PyQtGraph绘制股票行情K线图