当前位置: 首页 > news >正文

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.2 hutool-5.6.x支持 poi-ooxml 版本高于 5.0.0 xercesImpl版本高于 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 中添加数据。有两个需要注意的地方:

  1. 表格的表头会按照数组中第一个 Map 的 key 生成,比如说:我在第二个 Map 中存在一个 key 为性别的值,但第一个 Map 中并没有性别 key,那么生成的表格中将不会有性别这一列,生成的表头只以数组的第一个 Map 的 key 生成。
  2. 这种方式生成的表格,列的顺序是没法控制的。
// 生成一个新的 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,主要注意以下两点:

  1. 生成列的顺序和定义实体字段的顺序一致。
  2. @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 中可以认为存在以下几种类型:

  1. CellType.NUMERIC 数值类型,包括:整数、小数、日期、时间。
  2. CellType.STRING 字符串类型,包括:任何值、序列、文本长度。
  3. CellType.FORMULA 公式类型,包括:自定义。
  4. CellType.BLANK 空值,只要是单元格为空,都是这个类型。
  5. CellType.BOOLEAN 布尔类型。(没怎么用到这个)
  6. 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)

必读信息 该篇文章&#xff0c;主要通过 Java 代码对 Excel 文件的常用操作&#xff0c;包括&#xff1a;生成表格、修改单元格样式、设置数据有效性。 该篇文章&#xff0c;在官网文献下增加个人的看法和理解&#xff0c;如文中有出现不符、错误或需要补充的地方&#xff0c…...

YOLOv5算法改进(11)— 主干网络介绍(MobileNetV3、ShuffleNetV2和GhostNet)

前言:Hello大家好,我是小哥谈。主干网络通常指的是深度学习中的主干模型,通常由多个卷积层和池化层组成,用于提取输入数据的特征。在训练过程中,主干网络的参数会被不断优化以提高模型的准确性。YOLOv5算法中的主干网络可以有多种替换方案,为了后面讲解的方便,本篇文章就…...

ideal远程Debug部署在服务器上的服务详解

ideal远程Debug部署在服务器上的服务详解 一 简介二 ideal配置步骤第一步&#xff1a;点击Edit Configurations选项添加远程连接第二步&#xff1a;配置Remote JVM debug参数第三步&#xff1a;服务的启动参数中添加第二步生成的命令并重新启动服务第四步&#xff1a;ideal启动…...

基于SSM的校园音乐平台系统

基于SSM的校园音乐平台系统~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登录界面 管理员界面 歌手管理 歌曲管理 摘要 校园音乐平台系统&#xff08;Campus Mu…...

07_03文件系统怎么玩的

文件系统 Linux将文件系统分为了两层&#xff1a;VFS&#xff08;虚拟文件系统&#xff09;、具体文件系统&#xff0c;如下图所示&#xff1a; VFS&#xff08;Virtual Filesystem Switch&#xff09;称为虚拟文件系统或虚拟文件系统转换&#xff0c;是一个内核软件层&#…...

php实战案例记录(24)不要键名只保留值的算法

php中对数组 $originalArray array( “name” > “John”, “age” > 25, “city” > “New York” )仅去除键名保留值的算法是什么 array_values() 函数 在 PHP 中&#xff0c;你可以使用 array_values() 函数来去掉数组的键名。该函数会返回一个新数组&#xff0c…...

【交付高质量,用户高增长】-用户增长质量保证方法论 | 京东云技术团队

前言 俗话说&#xff0c;“测试是质量的守护者”&#xff0c;但单凭测试本身却远远不够。大多数情况下&#xff0c;测试像“一面镜子”&#xff0c;照出系统的面貌&#xff0c;给开发者提供修改代码的依据&#xff0c;这个“照镜子”的过程&#xff0c;就是质量评估的过程&…...

LMI FocalSpec 3D线共焦传感器 使用笔记1

一.硬件介绍 以上特别注意: 屏蔽线必须接地,因为在现场实际调试中,使用软件调试发现经常 弹窗 传感器丢失警告!! 以上 Position LED 的灯被钣金挡住,无法查看异常现象,能否将指示灯设置在软件界面上? 需要确认是软触发还是硬触发,理论上 硬触发比软触发速度要快.(我们目前使用…...

四、RocketMQ发送普通消息、批量消息和延迟消息

Producer发送普通消息的方式 1.同步发送消息 同步消息代表发送端发送消息到broker之后&#xff0c;等待消息发送结果后&#xff0c;再次发送消息 实现步骤 创建生产端&#xff0c;声明在哪个生产组注册NameServer地址构建Message实体&#xff0c;指定topic、tag、body启动…...

idea自定义 postfix completion提高编码效率

postfix completion的使用 详情见&#xff1a; https://www.cnblogs.com/expiator/p/17380495.html 自定义 postfix completion List、 String 初始化list&#xff1a; key: list表达式&#xff1a; List<$EXPR$> $END$List new ArrayList<>();字符串判空&…...

解锁学习电路设计的正确姿势!

...

【Linux】 ps命令使用

作为一个后端的程序员&#xff0c;我们经常用到ps -ef | grep XXX 到底什么事ps呢。 下面我们一起学习一下吧、 ps &#xff08;英文全拼&#xff1a;process status&#xff09;命令用于显示当前进程的状态&#xff0c;类似于 windows 的任务管理器。 ps命令 -Linux手册页 …...

打造高效的分布式爬虫系统:利用Scrapy框架实现

在大数据时代的今天&#xff0c;爬虫系统成为了获取和分析海量数据的重要工具。本文将介绍如何使用Scrapy框架来构建一个高效的分布式爬虫系统&#xff0c;以加速数据采集过程和提高系统的可扩展性。 Scrapy框架简介 Scrapy是一个基于Python的强大的开源网络爬虫框架&#xff…...

SpringCloud组件Ribbon的IRule的问题排查

最近很久没有写文章啦&#xff0c;刚好遇到了一个问题&#xff0c;其实问题也挺简单&#xff0c;但是还是得对源码有一定了解才能够发现。 最近在实现一个根据请求流量的标签&#xff0c;将请求转发到对应的节点&#xff0c;其实和俗称的灰度请求有点相似&#xff0c; 实现思…...

比较完整一些chatGPT项目代码(权威)

https://gitee.com/zccbbg/chatgpt-springboot-service yml中的配置文件无法读取&#xff0c;前端访问比较困难。...

Python - 生成二维码、条形码

二维码 import qrcode# 要生成的文本或链接 data "要生成的文本或链接"# 创建QR码对象 qr qrcode.QRCode(version1, # 版本号&#xff0c;通常设置为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”&#xff0c;这篇文章于2022年9月23日发表在Front Immunol 期刊…...

GPU高性能面试-写一个ReduceKernel

要求写一个reduceKernel 要求给出Kerne的完整调用: 1. 进行一维reduce 可以写一个最基础的&#xff0c;仅仅实现基础功能就行 使用share mem进行功能优化 使用shuffles指令完成block reduce操作 2.实现二维reduce...

深入探索STARK的安全性和可靠性——STARKs全面安全分析

1. 引言 non-interactive STARKs&#xff0c;起源于Interactive Oracle Proofs (IOPs)&#xff0c;然后通过random oracle模式转换为非交互式。StarkWare团队 ethSTARK Documentation – Version 1.2&#xff08;2023年7月&#xff09;论文做了更新&#xff0c;给出了完整具体…...

WPF 控件分辨率自适应问题

WPF 控件分辨率自适应时&#xff0c;我首先想到的是使用ViewBox控件来做分辨率自适应。 ViewBox这个控件通常和其他控件结合起来使用&#xff0c;是WPF中非常有用的控件。定义一个内容容器。ViewBox组件的作用是拉伸或延展位于其中的组件&#xff0c;以填满可用空间&#xff0…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...