前言
最近工作遇到一个Excel导出需求,需要实现下列几点
- 根据已有数据生产动态表头
- 对比数据前2列锁定,不允许编辑
- 表头和对比数据需要冻结,方便用户操作
- 可以下拉框选择数据
这几个需求很常见,都是基本的Excel操作,特意记录一下
依赖
以前导出我都是手动使用原生POI来进行操作,本次使用阿里的EasyExcel
使用Maven引用依赖会自动下载POI相关的依赖,不需要重复引用
1 |
|
编写代码实现需求
从官网查询导出示例,改改参数后
实现动态表头
1 |
|
通过上面的代码成功导出Excel,但是此时还只实现了动态表头,现在开始添加下拉框
添加下拉框以及隐藏列功能
实现SheetWriteHandler过滤器
SheetWriteHandler过滤器(处理器)的作用就是可以让我们自定义单元格的样式,我们常用的是其中的afterSheetCreate()方法,在该方法中,我们可以对当前所在的单元格进行样式的设置,先从百度copy一个模板来
1 |
|
只需要在构造函数中传入下拉框数据,并且在导出的时候把添加自定义的处理器就可以处理成功,此时调用代码如下
1 |
|
但是这里firstRow和lastRow是所有下拉列表的通用属性,会导致第三个需求不满足,他需要不可编辑,并且只是纯文本显示,不需要下拉框,所以我们要把它稍微改造一下,用对象来包装它
1 |
|
然后把上面的mapDropDown改成
1 |
|
然后把循环从map中获取数据改成循环从自定义的对象获取就好了,这样就能解决下拉框不对的问题
此时我们已经解决了2个问题,还需要冻结表头已经不可编辑部分表格
在过滤器中实现行列冻结
想要实现行列冻结,就需要在afterSheetCreate()方法中进行操作,该方法中有一个writeSheetHolder参数,表示当前写入表格的处理器,我们可以通过该处理器来获取到当前正在操作的sheet, 实现行列冻结的方法是createFreezePane(), 该方法有两种调用方式,源码如下:
1 |
|
第一种传入两个参数,含义是:
- var1 冻结的列数
- var2 冻结的行数
默认从第0行,第0列开始冻结,
第二种传入四个参数,含义是:
- var1 冻结的列数
- var2 冻结的行数
- var3 冻结后右边第一列的列号
- var4 冻结后下边第一行的行号 这种方法下冻结会更加的精确,
该方法在afterSheetCreate()下的具体使用如下:
1 |
|
此时我们表头也成功冻结成功,只差最后一步,不可编辑了
不可编辑
在POI中对单元格进行操作对应的是Cell,上面我们自定义的SheetWriteHandler是EasyExcel提供对Sheet操作的增强,同理肯定对Cell也有增强,那就是CellWriteHandler,现在我们来自定义一个,其中beforeCellCreate就是在Cell创建之前执行的动作,我们需要在这里初始化一些数据,当然都写在后文的afterCellCreate也可以,afterCellCreate就是创建好后进行增强
1 |
|
此时调用方还需要把这个新增的数据处理器加进去
1 |
|
然后此时我们再次进行测试,发现完美实现需求,但是会引入一个新问题,上面解锁 cellStyle.setLocked(false);只会解锁当前Excel中的数据,当我们新增数据,需要把Excel解锁,这个时候加锁的数据就会失效,我还没找到更好的办法-_-
后记
采用createExplicitListConstraint 进行设置枚举下拉,存在很大弊端,数据过多会导致无法显示,修复后所有的下拉框都会被删除.。
下面提供一个解决办法,通过将下拉数据存于隐藏的sheet中,通过excel中的公式进行设置,这样问题就得到解决。
关键代码
public class SheetHandler implements SheetWriteHandler {
/**
* 隐藏索引
*/
@Setter
@Getter
private List<Integer> hiddenIndices;
/**
* 下拉框值
*/
private List<DropDown> dropDownList;
public SheetHandler(List<DropDown> dropDownList) {
this.dropDownList = dropDownList;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
// 冻结表头
sheet.createFreezePane(0, 1, 0, 1);
dropDown(sheet);
hidden(sheet);
}
private void dropDown(Sheet sheet) {
if (CollectionUtils.isEmpty(dropDownList)) {
return;
}
DataValidationHelper helper = sheet.getDataValidationHelper();
String hiddenName = "hidden";
Workbook workbook = sheet.getWorkbook();
Sheet hidden = workbook.getSheet(hiddenName);
for (DropDown dropDown : dropDownList) {
/*** 起始行、终止行、起始列、终止列 **/
Integer firstRow = dropDown.getFirstRow();
Integer column = dropDown.getColumn();
CellRangeAddressList addressList =
new CellRangeAddressList(firstRow, dropDown.getLastRow() + firstRow - 1, column, column);
// 1.获取excel列名
String excelLine = getExcelLine(column);
// 2.循环赋值
List<String> data = dropDown.getData();
for (int i = 0, length = data.size(); i < length; i++) {
// 3:表示你开始的行数 3表示 你开始的列数
Row row = hidden.getRow(i);
if (row == null) {
row = hidden.createRow(i);
}
row.createCell(column).setCellValue(data.get(i));
}
// 4. =hidden!$H:$1:$H$50 sheet为hidden的 H1列开始H50行数据获取下拉数组
String refers = "=" + hiddenName + "!$" + excelLine + "$1:$" + excelLine + "$" + (data.size() + 1);
// 5 将刚才设置的sheet引用到你的下拉列表中
DataValidationConstraint constraint = helper.createFormulaListConstraint(refers);
DataValidation dataValidation = helper.createValidation(constraint, addressList);
sheet.addValidationData(dataValidation);
}
// 设置列为隐藏
int hiddenIndex = workbook.getSheetIndex(hiddenName);
if (!workbook.isSheetHidden(hiddenIndex)) {
workbook.setSheetHidden(hiddenIndex, true);
}
}
/**
* 返回excel列标A-Z-AA-ZZ
*
* @param num 列数
* @return java.lang.String
*/
public static String getExcelLine(int num) {
String line = "";
int first = num / 26;
int second = num % 26;
if (first > 0) {
line = (char)('A' + first - 1) + "";
}
line += (char)('A' + second) + "";
return line;
}
/**
* 设置隐藏列
*
* @param sheet
*/
private void hidden(Sheet sheet) {
if (!CollectionUtils.isEmpty(hiddenIndices)) {
// 设置隐藏列
for (Integer hiddenIndex : hiddenIndices) {
sheet.setColumnHidden(hiddenIndex, true);
}
}
}
}
调用
1 |
|