关于使用JavaPOI 导出Excel多级联动的一些方法
关于使用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数据的方法。
- 创建WorkBook
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(); - 创建Sheet页
XSSFSheet xssfSheet = xssfWorkbook.createSheet(sheetName);
xssfWorkbook.setSheetHidden(xssfWorkbook.getSheetIndex(sheetName), true);
//sheetName为String类型,就是一个名字。
//setSheetHidden的作用是设置sheet是否隐藏。 true:隐藏,false:显示 - 创建Row
XSSFRow xssfRow = xssfSheet.createRow(0);
//0代表创建第一行,Excel中行计数和列计数都是从0开始。 - 创建当前行的列
XSSFCell xssfCell = xssfRow .createCell(0);
//0代表创建第一列即第A列
xssfCell .setCellValue();
//为当前行的当前列设置值 - 设置数据规则(是应该这么说?)
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行的数据进行绑定,供后期使用。 - 设置数据有效性(此处可以简单理解为设置下拉框)
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)创建workbook HSSFWorkbook hssfWorkBook = new HSSFWorkbook(); // 2)创建sheet HSSFSheet 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看看。
- 关于Java Web 使用 POI 将 数据库表 导出 Excel 的完整实例
- java使用poi导出Excel的方法
- Java使用POI将数据导出到Excel的工具方法
- 关于java使用poi读取Excel文档使某些行或列为空时的处理方法
- Java使用POI实现数据导出excel报表
- 关于java poi导入导出excel的包冲突问题
- 请教一些关于java操作excel问题(如jxl.write.WritableWorkbook.write()方法)
- java 语言使用poi导出excel
- JAVA使用POI根据模版导出EXCEL
- java操作Excel之POI(5)利用POI实现使用模板批量导出数据
- 关于poi操作excel我使用的一些修饰操作
- 关于POI把数据库结果集导出到EXCEL和WORD的方法
- JAVA使用POI根据模版导出EXCEL
- JAVA使用POI根据模版导出EXCEL
- java使用poi或者jxl实现excel导出之后如何弹出保存提示框
- java使用poi从数据库导出Excel
- 关于Java使用POI对Excel2003和2007的导入和导出
- Java中使用poi导入、导出Excel
- 关于GridView导出Excel的一些问题(采用Ajax出现的的问题及解决方法)
- 关于javapoi导出Excel的Demo