您的位置:首页 > 编程语言 > Java开发

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

2019-04-19 19:42 417 查看

关于使用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)创建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看看。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: