1.问题描述

工作中遇到需要使用Java poi读写Excel文件的问题,因为需求中有要求在写文件时创建下拉选择框。按照传统的直接使用List集合保存下拉选择框的选项,再通过poi本身的方法将选择框的选项添加到下拉框中。

一开始编写demo测试时只有几个选项,可以正确的添加下拉框。后来把方法搬到项目中,发现创建的文件中的下拉框的选项数据不能正确的显示。通过对比两个文件,发现唯一的不同点就是下拉框选项的数量存在差异。通过一番查询,发现poi添加下拉框选项的数量被控制在20个以内(官方说法好像是256个字节?记不太清楚了,如果说错了,请大佬批评指正orz)

2.选项数量(<=20)较少的方法

  • 向Excel中写数据,简单样例
/**
     * 将lines的内容写入excel中
     *
     * @param outputPath excel存放路径
     * @param lines      需要写入execl的文件
     * @throws IOException
     */
    public synchronized void writeInExcel(String outputPath, Collection<String[]> lines) throws IOException {

        @SuppressWarnings("resource")
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 写入Excel表格开头
        Row firstRow = sheet.createRow(0);
        ArrayList<String> arrayList = new ArrayList<String>();
        String[] excelHeadList = {"a","b","c","d","e"};
        for (int i = 0; i < excelHeadList.length; i++) {
            arrayList.add(excelHeadList[i]);
        }
        String[] arr = new String[arrayList.size()];
        for (int i = 0; i < arrayList.size(); i++) {
            arr[i] = arrayList.get(i);
        }
        writeInRow(firstRow, arr);

        if (lines != null && lines.size() > 0) {
            // 写入Excel表格内容
            AtomicInteger i = new AtomicInteger(1);
            lines.forEach(line -> {
                Row row = sheet.createRow(i.get());
                writeInRow(row, line);
                i.set(i.incrementAndGet());
            });
            DataValidationHelper helper = sheet.getDataValidationHelper();
            // 创建审核结果下拉框
            createDropDownBox(sheet, helper, checkResultList, 1, i.get() - 1, arr.length - 6, arr.length - 6);
        }
        // 写入指定的文件
        workbook.write(new FileOutputStream(outputPath));
    }
  • 给Excel添加下拉框的方法
/**
     * 给Excel的添加下拉框
     *
     * @param sheet
     * @param helper
     * @param list     下拉框需要的数据
     * @param firstRow 首行
     * @param lastRow  尾行
     * @param firstCol 首列
     * @param lastCol  尾列
     */
    private void createDropDownBox(Sheet sheet, DataValidationHelper helper, String[] list, int firstRow, int lastRow,
                                   int firstCol, int lastCol) {
        CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);

        DataValidationConstraint constraint = helper.createExplicitListConstraint(list);
        DataValidation validation = helper.createValidation(constraint, addressList);
        if (validation instanceof XSSFDataValidation) {
            validation.setSuppressDropDownArrow(true);
            validation.setShowErrorBox(true);
        } else {
            validation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(validation);
    }
  • 把数组的内容写到Excel的行
/**
     * 将数组中的内容写入excel的行
     *
     * @param row  行
     * @param line 需要写入的数据
     */
    public static void writeInRow(Row row, String[] line) {
        if (line != null) {
            for (int i = 0; i < line.length; i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(line[i]);
            }
        }
    }

通过以上三个方法就可以实现向Excel中写入数据,并给指定的列添加下拉框

  • 全部代码
package com.learning;

import org.apache.poi.hssf.util.CellRangeAddressList;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: alex
 * @date: 2020/8/7 下午5:55
 */

public class Test {
    /**
     * 将lines的内容写入excel中
     *
     * @param outputPath excel存放路径
     * @param lines      需要写入execl的文件
     * @throws IOException
     */
    public synchronized void writeInExcel(String outputPath, Collection<String[]> lines) throws IOException {

        @SuppressWarnings("resource")
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 写入Excel表格开头
        Row firstRow = sheet.createRow(0);
        ArrayList<String> arrayList = new ArrayList<>();
        // 创建单元格标题数组
        String[] excelHeadList = {"row1","row2","row3","row4","row5"};
        for (int i = 0; i < excelHeadList.length; i++) {
            arrayList.add(excelHeadList[i]);
        }
        String[] arr = new String[arrayList.size()];
        for (int i = 0; i < arrayList.size(); i++) {
            arr[i] = arrayList.get(i);
        }
        writeInRow(firstRow, arr);

        String[] selectList = {"option1","option2","option3","option4","option5"};

        if (lines != null && lines.size() > 0) {
            // 写入Excel表格内容
            AtomicInteger i = new AtomicInteger(1);
            lines.forEach(line -> {
                Row row = sheet.createRow(i.get());
                writeInRow(row, line);
                i.set(i.incrementAndGet());
            });
            DataValidationHelper helper = sheet.getDataValidationHelper();
            // 创建审核结果下拉框
            createDropDownBox(sheet, helper, selectList, 1, i.get() - 1, arr.length - 6, arr.length - 6);
        }
        // 写入指定的文件
        workbook.write(new FileOutputStream(outputPath));
    }

    /**
     * 给Excel的添加下拉框
     *
     * @param sheet
     * @param helper
     * @param list     下拉框需要的数据
     * @param firstRow 首行
     * @param lastRow  尾行
     * @param firstCol 首列
     * @param lastCol  尾列
     */
    private void createDropDownBox(Sheet sheet, DataValidationHelper helper, String[] list, int firstRow, int lastRow,
                                   int firstCol, int lastCol) {
        CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);

        DataValidationConstraint constraint = helper.createExplicitListConstraint(list);
        DataValidation validation = helper.createValidation(constraint, addressList);
        if (validation instanceof XSSFDataValidation) {
            validation.setSuppressDropDownArrow(true);
            validation.setShowErrorBox(true);
        } else {
            validation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(validation);
    }

    /**
     * 将数组中的内容写入excel的行
     *
     * @param row  行
     * @param line 需要写入的数据
     */
    public static void writeInRow(Row row, String[] line) {
        if (line != null) {
            for (int i = 0; i < line.length; i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(line[i]);
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("hello world!!");
        Test test = new Test();
        List<String[]> list = new ArrayList<>();
        String[] strings = {"aaa","bbb","ccc","ddd","eee"};
        list.add(strings);
        try {
            test.writeInExcel("result/test.xlsx",list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

实现效果如下图所示:

Java文本框添加下拉框 java下拉选择框_poi

3.下拉框选项的数量超过了20个

上边的方法只适用于下拉框选项的数量在20个以内的情况,如果选项的数量超过了20个,那么需要使用隐藏sheet的方式来实现。即:使用一个隐藏的sheet表来存储下拉框的选项数量,再通过定位引用的方式来获取到选项的值。

只需要在上面的writeInExcel方法中增加下面的代码段嘛,即可实现在Excel中创建一个隐藏的sheet。

// 创建一个隐藏的sheet,存放下拉框选项
Sheet hiddenSheet = workbook.createSheet("hiddenSelect");
//  把下拉框列表数据放进隐藏sheet
Cell cell = null;
for (int i = 0; i < selectList.length; i++) {
   Row row = hiddenSheet.createRow(i);
   cell = row.createCell(0);
   cell.setCellValue(selectList[i]);
}
Name nameCell = workbook.createName();
nameCell.setNameName(hiddenSheet.getSheetName());
nameCell.setRefersToFormula(hiddenSheet.getSheetName() + "!$A$1:$A$" + selectList.length);
workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);

创建了隐藏的sheet,并进行定位之后,我们还需要对createDropDownBox方法进行修改:

/**
     * 给Excel的添加下拉框
     *
     * @param sheet
     * @param helper
     * @param list     下拉框需要的数据
     * @param firstRow 首行
     * @param lastRow  尾行
     * @param firstCol 首列
     * @param lastCol  尾列
     */
private void createDropDownBox(Sheet sheet, DataValidationHelper helper, String[] list, int firstRow, int lastRow,
                               int firstCol, int lastCol) {
   CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
   DataValidationConstraint constraint = null;
   DataValidation validation = null;
   // 为了适配xls和xlsx不同版本的Excel(即2003和2007版本的)
   if (sheet instanceof XSSFSheet || sheet instanceof SXSSFSheet) {
      constraint = helper.createFormulaListConstraint(hiddenSheet.getSheetName());
      validation = helper.createValidation(constraint, addressList);
   } else {
      constraint = DVConstraint.createFormulaListConstraint(hiddenSheet.getSheetName());
      validation = new HSSFDataValidation(addressList, constraint);
   }
   if (validation instanceof XSSFDataValidation) {
      validation.setSuppressDropDownArrow(true);
      validation.setShowErrorBox(true);
   } else {
      validation.setSuppressDropDownArrow(false);
   }
   sheet.addValidationData(validation);
}

通过以上的方法就可以实现超过20个选项数量的下拉框的创建。

真正用到项目中,还发现了一个问题:在2003版的Exce(即后缀名为:xls)中是可以看见隐藏sheet表的名称的,但是在新版的Office中(即后缀名为:xlsx)中,是看不见隐藏sheet表的。如下图所示:

Java文本框添加下拉框 java下拉选择框_java_02

我还是太菜了,不知道是什么原因。如果有大佬知道,请指教。

如果有错误欢迎大家指出,有疑问欢迎交流讨论!