Java 操作 Excel:生成数据、设置单元格样式、设置数据有效性(hutool)
必读信息
该篇文章,主要通过 Java 代码对 Excel 文件的常用操作,包括:生成表格、修改单元格样式、设置数据有效性。
该篇文章,在官网文献下增加个人的看法和理解,如文中有出现不符、错误或需要补充的地方,欢迎指正,非常感谢。
该篇文章操作 Excel 使用了 hutool 的工具包以及 poi 的依赖,其中 hutool 是一个超级无敌宇宙 perfect 的一个工具包,建议每一个 Java 程序员都要了解下(不是广告,真的不是广告 😊)。
-
hutool 官方文档地址:https://hutool.cn/docs
-
hutool API 文档地址:https://apidoc.gitee.com/dromara/hutool
首先给出下面所有案例代码使用的依赖:
-
gradle 项目
// hutool 依赖,我这里使用的是目前最新的版本 5.8.21 implementation 'cn.hutool:hutool-all:5.8.21' // 操作 Excel 的必须依赖 implementation 'org.apache.poi:poi-ooxml:5.2.3' implementation 'xerces:xercesImpl:2.12.2' // 日志依赖,非必须引入,如果项目中已经引入过那就去掉下面两个依赖 testImplementation 'io.basc.framework:log4j2:1.8.3' implementation 'org.apache.logging.log4j:log4j-core:2.20.0' -
maven 项目
<!-- hutool 依赖,我这里使用的是目前最新的版本 5.8.21 --> <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.21</version> </dependency> <!-- 操作 Excel 的必须依赖 --> <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version> </dependency> <dependency><groupId>xerces</groupId><artifactId>xercesImpl</artifactId><version>2.12.2</version> </dependency> <!-- 日志依赖,非必须引入,如果项目中已经引入那就无需引入下面两个依赖 --> <dependency><groupId>io.basc.framework</groupId><artifactId>log4j2</artifactId><version>1.8.3</version> </dependency> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.20.0</version> </dependency>
非常重要的提示:hutool 的版本与 poi-ooxml、xercesImpl 要对应,不然你会看见你代码都是红色的,版本选择参考 hutool 文档下的这句话:
说明 hutool-4.x 的
poi-ooxml版本需高于3.17(别问我 3.8 版本为啥不行,因为 3.17 > 3.8 ) hutool-5.x的poi-ooxml版本需高于4.1.2hutool-5.6.x支持poi-ooxml版本高于5.0.0xercesImpl版本高于2.12.0(非必须)
生成表格
用 hutool 生成 Excel 超级超级简单,在 hutool 官方文档上也给出了多种生成 Excel 的案例方法:https://hutool.cn/docs/#/poi/Excel%E7%94%9F%E6%88%90-ExcelWriter
List 生成表格数据
通过 List 写入数据,可以一次写入一行,也可以一次性写入多行,下面代码用了一些 hutool 的工具类,需要注意引入的 import 。
下面有几个比较重要的方法:
- ExcelUtil.getWriter():新建一个空的 Excel 文件,可以传入布尔值,true / false 生成 xlsx / xls 文件。
- writer.writeHeadRow():该方法用于写入表头数据,之所以用这个方法写入表头,可以方便设置表头的样式。
- writer.writeRow():向表格中写入单行数据。
- writer.write():向表格中写入数据,该方法有很多重载方法。
- writer.flush():将 excel 文件写入到本地磁盘。
- writer.close():资源释放,流操作完都要进行释放。
package top.shijialeya.hutool;import cn.hutool.core.lang.UUID;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** @author 17279*/
public class ExcelDemo01 {public static void main(String[] args) {// 生成一个新的 excel 文件(参数:true 生成 xlsx 文件;false 生成 xls 文件;)ExcelWriter writer = ExcelUtil.getWriter(true);// 向 excel 中写入表头信息writer.writeHeadRow(Arrays.asList("唯一标识", "账户编码", "用户姓名", "账号状态"));// 一次性向 excel 中写入一行数据List<String> rowData = Arrays.asList(UUID.randomUUID().toString(true), "001", "派大星", "正常状态");writer.writeRow(rowData);// 一次性向 excel 中写入多行数据List<List<String>> multiRowData = new ArrayList<>();multiRowData.add(Arrays.asList(UUID.randomUUID().toString(true), "002", "海绵宝宝", "正常状态"));multiRowData.add(Arrays.asList(UUID.randomUUID().toString(true), "003", "章鱼哥", "正常状态"));multiRowData.add(Arrays.asList(UUID.randomUUID().toString(true), "004", "瘸老板", "禁用状态"));writer.write(multiRowData);// 将 excel 文件保存到本地writer.flush(new File("D:\\test.xlsx"));// 资源释放writer.close();}
}
生成的文件如下:

Map 生成表格数据
通过 Map 的键值对向 Excel 中添加数据。有两个需要注意的地方:
- 表格的表头会按照数组中第一个 Map 的 key 生成,比如说:我在第二个 Map 中存在一个 key 为性别的值,但第一个 Map 中并没有性别 key,那么生成的表格中将不会有性别这一列,生成的表头只以数组的第一个 Map 的 key 生成。
- 这种方式生成的表格,列的顺序是没法控制的。
// 生成一个新的 excel 文件(参数:true 生成 xlsx 文件;false 生成 xls 文件;)
ExcelWriter writer = ExcelUtil.getWriter(true);// 创建 Map 的集合对象
List<Map<String, Object>> rowData = new ArrayList<>();
rowData.add(new HashMap<String, Object>() {{put("唯一标识", UUID.randomUUID().toString(true));put("账户编码", "001");put("用户姓名", "派大星");put("账号状态", "正常状态");
}});
rowData.add(new HashMap<String, Object>() {{put("唯一标识", UUID.randomUUID().toString(true));put("账户编码", "002");put("用户姓名", "海绵宝宝");put("账号状态", "正常状态");
}});
rowData.add(new HashMap<String, Object>() {{put("唯一标识", UUID.randomUUID().toString(true));put("账户编码", "003");put("用户姓名", "章鱼哥");put("账号状态", "正常状态");
}});
rowData.add(new HashMap<String, Object>() {{put("唯一标识", UUID.randomUUID().toString(true));put("账户编码", "004");put("用户姓名", "瘸老板");put("账号状态", "禁用状态");
}});// 一次性写出内容,使用默认样式
writer.write(rowData);// 将 excel 文件保存到本地
writer.flush(new File("D:\\test.xlsx"));// 资源释放
writer.close();
生成的文件如下:

实体类生成表格数据
通过自定的实体对象生成 Excel,主要注意以下两点:
- 生成列的顺序和定义实体字段的顺序一致。
- @Alias() 注解可以用于设置实体属性与列的别名,如果没有设置那么列名为实体的属性名称。除此之外,还可以通过方法 writer.addHeaderAlias(实体字段名称, 字段别名) 来灵活设置别名。
// 自定的实体对象
import cn.hutool.core.annotation.Alias;public class User {@Alias(value = "唯一标识")private String id;@Alias(value = "账户编码")private String code;@Alias(value = "用户姓名")private String username;@Alias(value = "账号状态")private String status;// 【注意】 getter\setter 方法这里省略,自己生成一下就好了public User(String id, String code, String username, String status) {this.id = id;this.code = code;this.username = username;this.status = status;}
}
// 生成一个新的 excel 文件(参数:true 生成 xlsx 文件;false 生成 xls 文件;)
ExcelWriter writer = ExcelUtil.getWriter(true);// 创建集合对象
List<User> userList = new ArrayList<>();
userList.add(new User(UUID.randomUUID().toString(true), "001", "派大星", "正常状态"));
userList.add(new User(UUID.randomUUID().toString(true), "002", "海绵宝宝", "正常状态"));
userList.add(new User(UUID.randomUUID().toString(true), "003", "章鱼哥", "正常状态"));
userList.add(new User(UUID.randomUUID().toString(true), "004", "瘸老板", "禁用状态"));// 自定义别名
// writer.addHeaderAlias("id", "唯一标识");
// writer.addHeaderAlias("code", "账户编码");
// ...// 写出内容
writer.write(userList);// 将 excel 文件保存到本地
writer.flush(new File("D:\\test.xlsx"));// 资源释放
writer.close();
生成的文件如下:

指定单元格生成数据
向指定位置的单元格写入指定的数据,这种方式一般很少用。不过这种方式能满足绝大部分的应用场景。
主要使用了下面的这个方法:
- excel.writeCellValue():向指定单元格中写入数据,方法有三个参数:列号、行号、单元格的值,行号 0 开始
// 生成一个新的 excel 文件(参数:true 生成 xlsx 文件;false 生成 xls 文件;)
ExcelWriter writer = ExcelUtil.getWriter(true);// 一次性向 excel 中写入多行数据
List<List<String>> rowData = new ArrayList<List<String>>() {{add(Arrays.asList("唯一标识", "账户编码", "用户姓名", "账号状态"));add(Arrays.asList(UUID.randomUUID().toString(true), "001", "派大星", "正常状态"));add(Arrays.asList(UUID.randomUUID().toString(true), "002", "海绵宝宝", "正常状态"));add(Arrays.asList(UUID.randomUUID().toString(true), "003", "章鱼哥", "正常状态"));add(Arrays.asList(UUID.randomUUID().toString(true), "004", "瘸老板", "禁用状态"));
}};// 遍历每一行的数据,索引 0 开始
for (int rowNum = 0; rowNum < rowData.size(); rowNum++) {List<String> cellData = rowData.get(rowNum);// 遍历该行每一个单元格,索引 0 开始for (int cellNum = 0; cellNum < cellData.size(); cellNum++) {// 向每一个单元格中设置值(参数:列号、行号、单元格的值,行号 0 开始)writer.writeCellValue(cellNum, rowNum, cellData.get(cellNum));}
}// 将 excel 文件保存到本地
writer.flush(new File("D:\\test.xlsx"));// 资源释放
writer.close();
生成的文件如下:

修改单元格样式
这个修改表格的样式感觉工具里面有很多 bug,遇到的问题我会一一列出来。
行高和列宽
-
设置默认的行高,所有的行高
writer.setDefaultRowHeight(height)行高范围:0 ~ 409,为 0 时表示隐藏。 -
指定列的列宽
writer.setColumnWidth(columnIndex, width)列宽范围:0 ~ 255 字符,0 表示为隐藏,列号从 0 开始。 -
指定行的行高
writer.setRowHeight(rownum, height)行高范围:0 ~ 409,为 0 时表示隐藏,行号从 0 开始。
需要特别注意的是:setDefaultRowHeight() 和 setRowHeight() 方法不能一起使用,当两者同时出现时,只会生效 setRowHeight() 方法的内容,而 setDefaultRowHeight() 完全不起效果。
案例代码:
// 指定默认的行高
writer.setDefaultRowHeight(25);
// 给指定列设置列宽,列的索引 0 开始(宽度单位:字符 1 ~ 256)
writer.setColumnWidth(0, 36);
// 给指定行设置行高,与 setDefaultRowHeight() 选择其一
// writer.setRowHeight(1, 35);
结果:

表头样式
在 hutool 操作 excel 有一个表头的概念
- 通过 writer.writeHeadRow() 写入表格数据,这个数据就是表头的数据。
- 通过 writer.write() 写入 Map 类型的数据,Map 的 key 也是表头数据。
- 通过实体对象写入表格,注解 @Alias 指定的名称也为表头数据。
- 当通过 writer.writeCellValue() 方法写入的数据不属于表头。
默认生成的表头是灰底黑字居中的样式:

可以对表头的样式进行修改:
// 获得表头单元格样式对象
CellStyle headCellStyle = writer.getHeadCellStyle();
// 设置表头前景色
headCellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
// 创建字体样式
Font headCellFont = writer.createFont();
// 设置字体颜色
headCellFont.setColor(IndexedColors.DARK_RED.index);
// 设置字体大小
headCellFont.setFontHeightInPoints((short) 12);
// 设置字体类型
headCellFont.setFontName("Microsoft YaHei UI");
// 设置字体加粗
headCellFont.setBold(true);
// 设置斜体
headCellFont.setItalic(true);
// 设置删除线
headCellFont.setStrikeout(true);
headCellStyle.setFont(headCellFont);
效果如下:

设置全局样式
这种方式可以修改除表头以外含有数据的单元格样式,表头的样式不会修改。
// 设置前景色(下面两个语句)
rowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
rowStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
// 创建字体样式
Font cellFont = writer.createFont();
// 设置字体颜色
cellFont.setColor(IndexedColors.DARK_RED.index);
// 设置字体大小
cellFont.setFontHeightInPoints((short) 16);
// 设置字体类型
cellFont.setFontName("Microsoft YaHei UI");
rowStyle.setFont(cellFont);
效果如下:

设置行样式
这里有几个坑的地方:
- 设置背景色的时候只能用 setFillPattern() 和 setFillForegroundColor() 两个方法共同控制,setFillBackgroundColor() 和 setFillForegroundColor() 这两个方法不能无法修改背景的颜色。【这里巨坑】
- 在设置字体样式的时候,我们通过 writer.createFont() 创建的字体,这个字体不在 CellStyle 上,所以要重新执行 setFont() 和 setRowStyleIfHasData()。
- 跟 writer.setRowStyleIfHasData() 有一个类似的 setRowStyle() 方法,如果使用的是 setRowStyle() 方法,那么行单元格中存在数据的单元格的样式会被替换,也就是有内容的单元格样式不会有效果。【这里也巨坑】
案例代码:
// 获得第二行的样式
CellStyle rowStyle = writer.getOrCreateRowStyle(1);
// 设置前景色(下面两个语句)
rowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
rowStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
// 创建字体样式
Font cellFont = writer.createFont();
// 设置字体颜色
cellFont.setColor(IndexedColors.DARK_RED.index);
// 设置字体大小
cellFont.setFontHeightInPoints((short) 16);
// 设置字体类型
cellFont.setFontName("Microsoft YaHei UI");
rowStyle.setFont(cellFont);
// 设置第二行的表格样式(要重新设置 Style)
writer.setRowStyleIfHasData(1, rowStyle);
效果如下:

设置列样式
方式与设置单行样式相近,获取样式和设置样式的方法不一样:
writer.getOrCreateColumnStyle() 获取列的样式对象。
writer.setColumnStyleIfHasData() 方法有三个参数:列号、开始行号、样式,列号和行号都是 0 开始。
// 获得第二列的样式
CellStyle rowStyle = writer.getOrCreateColumnStyle(1);
// 设置前景色(下面两个语句)
rowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
rowStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
// 创建字体样式
Font cellFont = writer.createFont();
// 设置字体颜色
cellFont.setColor(IndexedColors.DARK_RED.index);
// 设置字体大小
cellFont.setFontHeightInPoints((short) 16);
// 设置字体类型
cellFont.setFontName("Microsoft YaHei UI");
rowStyle.setFont(cellFont);
// 设置第二列的表格样式(参数:列号、开始行号、样式,列号和行号都是 0 开始)
writer.setColumnStyleIfHasData(1, 1, rowStyle);
效果如下:

指定单元格样式
选定指定的单元格,修改单元格的样式:
// 获得第二行第二列的样式
CellStyle rowStyle = writer.createCellStyle(1, 1);
// 设置前景色(下面两个语句)
rowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
rowStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
// 创建字体样式
Font cellFont = writer.createFont();
// 设置字体颜色
cellFont.setColor(IndexedColors.DARK_RED.index);
// 设置字体大小
cellFont.setFontHeightInPoints((short) 16);
// 设置字体类型
cellFont.setFontName("Microsoft YaHei UI");
rowStyle.setFont(cellFont);
效果如下:

设置数据有效性
Excel 的数据有效性有以下几种:

上面有八种数据有效性(任何值、整数、小数、序列、日期、时间、文本长度、自定义),但在 POI 中可以认为存在以下几种类型:
CellType.NUMERIC数值类型,包括:整数、小数、日期、时间。CellType.STRING字符串类型,包括:任何值、序列、文本长度。CellType.FORMULA公式类型,包括:自定义。CellType.BLANK空值,只要是单元格为空,都是这个类型。CellType.BOOLEAN布尔类型。(没怎么用到这个)CellType.ERROR错误单元格。(没怎么用到这个)
整数
如下案例:设置单元格只能输入 1 ~ 100 的整数。
// 创建 Excel 文件
ExcelWriter writer = ExcelUtil.getWriter(new File("D:\\code\\xxx.xlsx"));
Sheet sheet = writer.getSheet();
// 插入表头数据
writer.writeHeadRow(new ArrayList<String>() {{add("年龄");
}});
// 设置数据有效性
String minNum = "1";
String maxNum = "100";
// 创建数据有效性助手对象
DataValidationHelper helper = sheet.getDataValidationHelper();
// 创建整数有效性
// 参数1:BETWEEN 介于两者之间;NOT_BETWEEN 不在两者之间;EQUAL 相等;NOT_EQUAL 不等;GREATER_THAN 大于;LESS_THAN 小于;GREATER_OR_EQUAL 大于等于;LESS_OR_EQUAL 小于等于;
DataValidationConstraint constraint = helper.createIntegerConstraint(DataValidationConstraint.OperatorType.BETWEEN, minNum, maxNum);
// 四个参数分别是:起始行、终止行、起始列、终止列(需要注意,因为表头已经占了一行,所以行以 1 开始)
DataValidation validation = helper.createValidation(constraint, new CellRangeAddressList(1, 65535, 0, 0));
validation.createErrorBox("输入有误", String.format("请输入%s~%s之间的整数", minNum, maxNum));
// 文件兼容处理【必须】
if (validation instanceof XSSFDataValidation) {validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);
} else {validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
// 资源释放
writer.close();
效果如下:

需要注意的地方:
- CellRangeAddressList(1, 65535, 0, 0) 方法处,这里没办法控制列下的所有行,只能通过指定范围的行,65535 还可以换成更大的数,行列的索引从 0 开始。
- 文件兼容处理的代码必须要加上,不然会出现不生效的问题。
小数
如下案例:设置单元格只能输入 1 ~ 3 的数值。
// 创建 Excel 文件
ExcelWriter writer = ExcelUtil.getWriter(new File("D:\\code\\xxx.xlsx"));
Sheet sheet = writer.getSheet();
// 插入表头数据
writer.writeHeadRow(new ArrayList<String>() {{add("身高(m)");
}});
// 设置数据有效性
String minNum = "0";
String maxNum = "3";
// 创建数据有效性助手对象
DataValidationHelper helper = sheet.getDataValidationHelper();
// 创建整数有效性
// 参数1:ANY 任意值;INTEGER 整数;DECIMAL 小数;LIST 列表;DATE 时间;TIME 时间;TEXT_LENGTH 文本长度;FORMULA 正则/公式;
// 参数2:BETWEEN 介于两者之间;NOT_BETWEEN 不在两者之间;EQUAL 相等;NOT_EQUAL 不等;GREATER_THAN 大于;LESS_THAN 小于;GREATER_OR_EQUAL 大于等于;LESS_OR_EQUAL 小于等于;
DataValidationConstraint constraint = helper.createNumericConstraint(DataValidationConstraint.ValidationType.DECIMAL,DataValidationConstraint.OperatorType.BETWEEN,minNum,maxNum
);
// 四个参数分别是:起始行、终止行、起始列、终止列(需要注意,因为表头已经占了一行,所以以 1 开始)
DataValidation validation = helper.createValidation(constraint, new CellRangeAddressList(1, 65536, 0, 0));
validation.createErrorBox("输入有误", String.format("请输入%s~%s之间的数值", minNum, maxNum));
// 文件兼容处理【必须】
if (validation instanceof XSSFDataValidation) {validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);
} else {validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
// 资源释放
writer.close();
效果如下:

序列/下拉
如下案例:添加下拉选择功能。
// 创建 Excel 文件
ExcelWriter writer = ExcelUtil.getWriter(new File("D:\\code\\xxx.xlsx"));
Sheet sheet = writer.getSheet();
// 插入表头数据
writer.writeHeadRow(new ArrayList<String>() {{add("序列/下拉");
}});
// 设置数据有效性
// 创建数据有效性助手对象
DataValidationHelper helper = sheet.getDataValidationHelper();
// 创建整数有效性
DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[]{"类型1", "类型2", "类型3"}
);
// 四个参数分别是:起始行、终止行、起始列、终止列(需要注意,因为表头已经占了一行,所以以 1 开始)
DataValidation validation = helper.createValidation(constraint, new CellRangeAddressList(1, 65536, 0, 0));
validation.createErrorBox("输入有误", "请选择下拉选项的值");
// 文件兼容处理【必须】
if (validation instanceof XSSFDataValidation) {validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);
} else {validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
// 资源释放
writer.close();
效果如下:

日期
如下案例:
// 创建 Excel 文件
ExcelWriter writer = ExcelUtil.getWriter(new File("D:\\code\\xxx.xlsx"));
Sheet sheet = writer.getSheet();
// 插入表头数据
writer.writeHeadRow(new ArrayList<String>() {{add("日期");
}});
// 设置数据有效性
String min = "date(1970,1,1)";
String max = "date(2024,12,32)";
// 创建数据有效性助手对象
DataValidationHelper helper = sheet.getDataValidationHelper();
// 创建整数有效性
// 参数1:BETWEEN 介于两者之间;NOT_BETWEEN 不在两者之间;EQUAL 相等;NOT_EQUAL 不等;GREATER_THAN 大于;LESS_THAN 小于;GREATER_OR_EQUAL 大于等于;LESS_OR_EQUAL 小于等于;
DataValidationConstraint constraint = helper.createDateConstraint(DataValidationConstraint.OperatorType.BETWEEN, min, max, "YYYY/MM/DD"
);
// 四个参数分别是:起始行、终止行、起始列、终止列(需要注意,因为表头已经占了一行,所以以 1 开始)
DataValidation validation = helper.createValidation(constraint, new CellRangeAddressList(1, 65536, 0, 0));
validation.createErrorBox("输入有误", String.format("请输入%s~%s范围内的日期值", "1970/1/1", "2024/12/32"));
// 文件兼容处理【必须】
if (validation instanceof XSSFDataValidation) {validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);
} else {validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
// 资源释放
writer.close();
效果如下:

特别注意:
- helper.createDateConstraint(OperatorType, min, max, dateFormat) 设置 min,max 时,一定要用 date() 函数,如 date(2020,12,12)
时间
案例如下:
// 创建 Excel 文件
ExcelWriter writer = ExcelUtil.getWriter(new File("D:\\code\\xxx.xlsx"));
Sheet sheet = writer.getSheet();
// 插入表头数据
writer.writeHeadRow(new ArrayList<String>() {{add("日期");
}});
// 设置数据有效性
String min = "time(9,0,0)";
String max = "time(18,0,0)";
// 创建数据有效性助手对象
DataValidationHelper helper = sheet.getDataValidationHelper();
// 创建整数有效性
// 参数1:BETWEEN 介于两者之间;NOT_BETWEEN 不在两者之间;EQUAL 相等;NOT_EQUAL 不等;GREATER_THAN 大于;LESS_THAN 小于;GREATER_OR_EQUAL 大于等于;LESS_OR_EQUAL 小于等于;
DataValidationConstraint constraint = helper.createTimeConstraint(DataValidationConstraint.OperatorType.BETWEEN, min, max
);
// 四个参数分别是:起始行、终止行、起始列、终止列(需要注意,因为表头已经占了一行,所以以 1 开始)
DataValidation validation = helper.createValidation(constraint, new CellRangeAddressList(1, 65536, 0, 0));
validation.createErrorBox("输入有误", String.format("请输入%s~%s范围内的时间值", "9:00:00", "18:00:00"));
// 文件兼容处理【必须】
if (validation instanceof XSSFDataValidation) {validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);
} else {validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
// 资源释放
writer.close();
效果如下:

特别注意:
- helper.createTimeConstraint() 传递的 time 值要用 time(),如:time(12,30,0)
文本长度
案例如下:
// 创建 Excel 文件
ExcelWriter writer = ExcelUtil.getWriter(new File("D:\\code\\xxx.xlsx"));
Sheet sheet = writer.getSheet();
// 插入表头数据
writer.writeHeadRow(new ArrayList<String>() {{add("字符");
}});
// 设置数据有效性
String min = "0";
String max = "10";
// 创建数据有效性助手对象
DataValidationHelper helper = sheet.getDataValidationHelper();
// 创建整数有效性
// 参数1:BETWEEN 介于两者之间;NOT_BETWEEN 不在两者之间;EQUAL 相等;NOT_EQUAL 不等;GREATER_THAN 大于;LESS_THAN 小于;GREATER_OR_EQUAL 大于等于;LESS_OR_EQUAL 小于等于;
DataValidationConstraint constraint = helper.createTextLengthConstraint(DataValidationConstraint.OperatorType.BETWEEN, min, max);
// 四个参数分别是:起始行、终止行、起始列、终止列(需要注意,因为表头已经占了一行,所以以 1 开始)
DataValidation validation = helper.createValidation(constraint, new CellRangeAddressList(1, 65536, 0, 0));
validation.createErrorBox("输入有误", String.format("请输入%s以内的字符数", max));
// 文件兼容处理【必须】
if (validation instanceof XSSFDataValidation) {validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);
} else {validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
// 资源释放
writer.close();
效果如下:

本篇文章加入了一些本人狭隘的想法和理解,如有出现错误之处,欢迎指正,非常感谢。
相关文章:
Java 操作 Excel:生成数据、设置单元格样式、设置数据有效性(hutool)
必读信息 该篇文章,主要通过 Java 代码对 Excel 文件的常用操作,包括:生成表格、修改单元格样式、设置数据有效性。 该篇文章,在官网文献下增加个人的看法和理解,如文中有出现不符、错误或需要补充的地方,…...
YOLOv5算法改进(11)— 主干网络介绍(MobileNetV3、ShuffleNetV2和GhostNet)
前言:Hello大家好,我是小哥谈。主干网络通常指的是深度学习中的主干模型,通常由多个卷积层和池化层组成,用于提取输入数据的特征。在训练过程中,主干网络的参数会被不断优化以提高模型的准确性。YOLOv5算法中的主干网络可以有多种替换方案,为了后面讲解的方便,本篇文章就…...
ideal远程Debug部署在服务器上的服务详解
ideal远程Debug部署在服务器上的服务详解 一 简介二 ideal配置步骤第一步:点击Edit Configurations选项添加远程连接第二步:配置Remote JVM debug参数第三步:服务的启动参数中添加第二步生成的命令并重新启动服务第四步:ideal启动…...
基于SSM的校园音乐平台系统
基于SSM的校园音乐平台系统~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登录界面 管理员界面 歌手管理 歌曲管理 摘要 校园音乐平台系统(Campus Mu…...
07_03文件系统怎么玩的
文件系统 Linux将文件系统分为了两层:VFS(虚拟文件系统)、具体文件系统,如下图所示: VFS(Virtual Filesystem Switch)称为虚拟文件系统或虚拟文件系统转换,是一个内核软件层&#…...
php实战案例记录(24)不要键名只保留值的算法
php中对数组 $originalArray array( “name” > “John”, “age” > 25, “city” > “New York” )仅去除键名保留值的算法是什么 array_values() 函数 在 PHP 中,你可以使用 array_values() 函数来去掉数组的键名。该函数会返回一个新数组,…...
【交付高质量,用户高增长】-用户增长质量保证方法论 | 京东云技术团队
前言 俗话说,“测试是质量的守护者”,但单凭测试本身却远远不够。大多数情况下,测试像“一面镜子”,照出系统的面貌,给开发者提供修改代码的依据,这个“照镜子”的过程,就是质量评估的过程&…...
LMI FocalSpec 3D线共焦传感器 使用笔记1
一.硬件介绍 以上特别注意: 屏蔽线必须接地,因为在现场实际调试中,使用软件调试发现经常 弹窗 传感器丢失警告!! 以上 Position LED 的灯被钣金挡住,无法查看异常现象,能否将指示灯设置在软件界面上? 需要确认是软触发还是硬触发,理论上 硬触发比软触发速度要快.(我们目前使用…...
四、RocketMQ发送普通消息、批量消息和延迟消息
Producer发送普通消息的方式 1.同步发送消息 同步消息代表发送端发送消息到broker之后,等待消息发送结果后,再次发送消息 实现步骤 创建生产端,声明在哪个生产组注册NameServer地址构建Message实体,指定topic、tag、body启动…...
idea自定义 postfix completion提高编码效率
postfix completion的使用 详情见: https://www.cnblogs.com/expiator/p/17380495.html 自定义 postfix completion List、 String 初始化list: key: list表达式: List<$EXPR$> $END$List new ArrayList<>();字符串判空&…...
解锁学习电路设计的正确姿势!
...
【Linux】 ps命令使用
作为一个后端的程序员,我们经常用到ps -ef | grep XXX 到底什么事ps呢。 下面我们一起学习一下吧、 ps (英文全拼:process status)命令用于显示当前进程的状态,类似于 windows 的任务管理器。 ps命令 -Linux手册页 …...
打造高效的分布式爬虫系统:利用Scrapy框架实现
在大数据时代的今天,爬虫系统成为了获取和分析海量数据的重要工具。本文将介绍如何使用Scrapy框架来构建一个高效的分布式爬虫系统,以加速数据采集过程和提高系统的可扩展性。 Scrapy框架简介 Scrapy是一个基于Python的强大的开源网络爬虫框架ÿ…...
SpringCloud组件Ribbon的IRule的问题排查
最近很久没有写文章啦,刚好遇到了一个问题,其实问题也挺简单,但是还是得对源码有一定了解才能够发现。 最近在实现一个根据请求流量的标签,将请求转发到对应的节点,其实和俗称的灰度请求有点相似, 实现思…...
比较完整一些chatGPT项目代码(权威)
https://gitee.com/zccbbg/chatgpt-springboot-service yml中的配置文件无法读取,前端访问比较困难。...
Python - 生成二维码、条形码
二维码 import qrcode# 要生成的文本或链接 data "要生成的文本或链接"# 创建QR码对象 qr qrcode.QRCode(version1, # 版本号,通常设置为1error_correctionqrcode.constants.ERROR_CORRECT_L, # 错误修正级别box_size10, # 每个小方块的像素大小bor…...
8+纯生信,多组机器学习+分型探讨黑色素瘤发文思路。
今天给同学们分享一篇泛癌多组机器学习分型的生信文章“Comprehensive characterisation of immunogenic cell death in melanoma revealing the association with prognosis and tumor immune microenvironment”,这篇文章于2022年9月23日发表在Front Immunol 期刊…...
GPU高性能面试-写一个ReduceKernel
要求写一个reduceKernel 要求给出Kerne的完整调用: 1. 进行一维reduce 可以写一个最基础的,仅仅实现基础功能就行 使用share mem进行功能优化 使用shuffles指令完成block reduce操作 2.实现二维reduce...
深入探索STARK的安全性和可靠性——STARKs全面安全分析
1. 引言 non-interactive STARKs,起源于Interactive Oracle Proofs (IOPs),然后通过random oracle模式转换为非交互式。StarkWare团队 ethSTARK Documentation – Version 1.2(2023年7月)论文做了更新,给出了完整具体…...
WPF 控件分辨率自适应问题
WPF 控件分辨率自适应时,我首先想到的是使用ViewBox控件来做分辨率自适应。 ViewBox这个控件通常和其他控件结合起来使用,是WPF中非常有用的控件。定义一个内容容器。ViewBox组件的作用是拉伸或延展位于其中的组件,以填满可用空间࿰…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...
