java eazyexcel 实现excel的动态多级联动下拉列表(2)使用MATCH+OFFSET函数

原理

  1. 同样是将数据源放到一个新建的隐藏的sheet中,第一行是第一个列表的数据,第二行是每一个有下级菜单的菜单,他下面的行就是他下级菜单的每一值
  2. 使用MATCH函数从第二行找到上级菜单对应的列
  3. 根据OFFSET函数从2中获取的列,取得下级菜单值列表

这样就解决了上一篇中的所有缺点,不过因为用了大量的函数,所以带下拉功能的行数(CascadeCellBO的rowNum)不要太多否则会卡。获取上级菜单用相对定位,数据源用绝对定位,这样复制单元格也可用级联下拉

代码

@Data
public class CellDataSourceBO implements Serializable {

    private static final long serialVersionUID = 8264756992270137029L;

    /**
     * 有子菜单的选项的个数(第二行的列数)对应的字母
     */
    private String selectMaxColStr;

    /**
     * 最大的选项行数
     */
    private int maxSelectRow;

    /**
     * 数据源名称(sheet名)
     */
    private String name;
}




public class CascadeWriteHandler implements SheetWriteHandler {

    private final AtomicInteger dataSourceIndex;
    private final List<CascadeCellBO> cascadeCellList;
    private final Map<List<NameCascadeBO>, CellDataSourceBO> dataSourceCache;

    public CascadeWriteHandler(List<CascadeCellBO> cascadeCellList) {
        this.cascadeCellList = cascadeCellList;
        this.dataSourceCache = new HashMap<>();
        this.dataSourceIndex = new AtomicInteger();
    }

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        //获取工作簿
        Sheet sheet = writeSheetHolder.getSheet();
        Workbook book = writeWorkbookHolder.getWorkbook();
        DataValidationHelper dvHelper = sheet.getDataValidationHelper();
        cascadeCellList.stream().filter(c -> c.getMaxLevel() > 0).forEach(cascadeCellBO -> {
            int maxLevel = cascadeCellBO.getMaxLevel();
            int colIndex = cascadeCellBO.getColIndex();
            int firstRowIndex = cascadeCellBO.getRowIndex();
            int lastRowIndex = firstRowIndex + cascadeCellBO.getRowNum();
            List<NameCascadeBO> nameCascadeList = cascadeCellBO.getNameCascadeList();
            //如果大类都没有,就渲染maxLevel个空的下拉列表
            if (nameCascadeList == null || nameCascadeList.isEmpty()) {
                DataValidationConstraint expConstraint = dvHelper.createExplicitListConstraint(new String[]{""});
                CellRangeAddressList expRangeAddressList = new CellRangeAddressList(firstRowIndex, lastRowIndex, colIndex, colIndex + maxLevel - 1);
                setValidation(sheet, dvHelper, expConstraint, expRangeAddressList, "提示", "你输入的值未在备选列表中,请下拉选择合适的值");
            } else {
                CellDataSourceBO cellDataSourceBO = buildOrGetDataSource(book
<think> 根据用户需求,用户希望了解如何使用Java实现Excel多级联动下拉列表多级联动下拉列表是指当一个单元格下拉列表选择后,下一个单元格下拉列表内容会根据前一个单元格的选择动态变化。例如,选择省份后,城市下拉列表只显示该省份下的城市。 在Java中操作Excel,常用的库有Apache POI和JExcelApi等。其中Apache POI功能强大,支持.xls和.xlsx格式,并且支持数据验证(DataValidation)功能,可以用来创建下拉列表。对于多级联动,我们需要使用数据验证中的“依赖列表”功能,即第二个下拉列表的内容依赖于第一个下拉列表的选择。 然而,需要注意的是,Apache POI本身不直接支持多级联动的数据验证(即动态改变下拉列表内容)。但我们可以通过以下两种方法来实现: 1. **使用隐藏的工作表(Sheet)存储关联数据**:在Excel中,我们可以将第二级及以后的数据放置在一个隐藏的工作表中,然后使用公式(如INDIRECT函数)来引用这些数据。在Apache POI中,我们可以创建隐藏的工作表,并在其中设置好关联数据,然后使用数据验证设置允许的范围为这些隐藏区域。 2. **使用命名区域(Named Ranges)**:为每一级的数据定义命名区域,然后在数据验证中引用这些命名区域。对于多级联动,我们可以通过定义多个命名区域,并使用INDIRECT函数来引用。 具体步骤(以两级联动为例): - 创建一个隐藏的工作表(例如“DataSheet”),在第一列存放一级列表(如省份),在后续列存放对应的二级列表(如每个省份下的城市)。 - 为二级列表的每一列定义一个命名区域,命名区域的名字与一级列表的选项相同(例如,一级列表选择“北京”,那么对应的城市列命名为“北京”)。 - 在数据验证中,一级下拉列表直接引用一级列表的区域。 - 在二级下拉列表的数据验证中,设置允许的公式为`=INDIRECT(一级单元格)`。这样当一级单元格选择后,二级下拉列表就会根据一级单元格的值(即命名区域的名称)找到对应的命名区域。 在Apache POI中,我们可以通过以下步骤实现: 1. 创建工作簿,包括一个主工作表(用于显示)和一个隐藏的数据工作表。 2. 在数据工作表中设置一级列表和对应的二级列表。 3. 为每个二级列表创建命名区域。 4. 在主工作表中,为一级列表设置数据验证(直接引用数据工作表中的一级列表区域)。 5. 为二级列表设置数据验证,使用公式`=INDIRECT(一级单元格的地址)`,这里一级单元格的地址需要是相对引用或绝对引用,根据实际情况确定。 注意:在Apache POI中,创建命名区域使用`workbook.createName()`方法,创建数据验证使用`DataValidationHelper`和`DataValidationConstraint`。 下面是一个简单的代码示例(使用Apache POI): </think> ### Java实现Excel多级联动下拉列表的方法 在Java实现Excel多级联动下拉列表(如省-市-县三级联动),主要通过Apache POI库结合Excel的数据验证(Data Validation)功能实现。以下是具体实现步骤: #### 核心实现方法 ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; public class ExcelMultiLevelDropdown { public static void main(String[] args) throws Exception { Workbook workbook = new XSSFWorkbook(); // 1. 创建隐藏数据表存储联动数据 Sheet dataSheet = workbook.createSheet("Data"); workbook.setSheetHidden(workbook.getSheetIndex(dataSheet), true); // 创建省份数据 (A列) Row provinceRow = dataSheet.createRow(0); provinceRow.createCell(0).setCellValue("北京"); provinceRow.createCell(1).setCellValue("上海"); provinceRow.createCell(2).setCellValue("广东"); // 创建城市数据 (B-D列) Row cityRow = dataSheet.createRow(1); cityRow.createCell(0).setCellValue("朝阳区"); // 北京 cityRow.createCell(1).setCellValue("海淀区"); cityRow.createCell(2).setCellValue("浦东区"); // 上海 cityRow.createCell(3).setCellValue("闵行区"); cityRow.createCell(4).setCellValue("广州市"); // 广东 cityRow.createCell(5).setCellValue("深圳市"); // 2. 创建主表 Sheet mainSheet = workbook.createSheet("Main"); // 3. 创建一级下拉(省份) DataValidationHelper dvHelper = mainSheet.getDataValidationHelper(); CellRangeAddressList provinceList = new CellRangeAddressList(0, 0, 0, 0); DataValidationConstraint provinceConstraint = dvHelper.createFormulaListConstraint("Data!$A$1:$C$1"); DataValidation provinceValidation = dvHelper.createValidation(provinceConstraint, provinceList); mainSheet.addValidationData(provinceValidation); // 4. 创建二级联动下拉(城市) Name namedRange = workbook.createName(); namedRange.setNameName("cityData"); namedRange.setRefersToFormula("OFFSET(Data!$A$2,0,MATCH(Main!A1,Data!$1:$1,0)-1,1,2)"); CellRangeAddressList cityList = new CellRangeAddressList(0, 0, 1, 1); DataValidationConstraint cityConstraint = dvHelper.createFormulaListConstraint("cityData"); DataValidation cityValidation = dvHelper.createValidation(cityConstraint, cityList); mainSheet.addValidationData(cityValidation); // 保存文件 FileOutputStream fos = new FileOutputStream("MultiLevelDropdown.xlsx"); workbook.write(fos); workbook.close(); fos.close(); } } ``` #### 关键技术要点: 1. **隐藏数据表**:创建隐藏工作表存储所有级联数据 2. **命名区域公式**:使用`OFFSET+MATCH`公式动态获取关联数据 ```excel OFFSET(Data!$A$2,0,MATCH(Main!A1,Data!$1:$1,0)-1,1,2) ``` 3. **数据验证绑定**: - 一级下拉:直接引用静态区域 - 二级下拉:引用动态命名区域 4. **POI关键类**: - `DataValidationHelper`:创建验证约束 - `DataValidationConstraint`:定义数据来源 - `Name`:创建命名公式区域 #### 实现效果: 1. 在A1单元格选择省份 2. B1单元格自动显示对应省份的城市列表 3. 支持无限扩展层级(需相应增加命名公式) > **注意**:实际应用中需处理动态数据范围,建议使用`COUNTA`函数计算数据范围长度,避免空值问题。完整项目可参考Apache POI官方示例[^1]。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值