null

Apache POI и Excel - создание фильтров для столбцов таблицы

Обнаружил интересный момент - оказывается, в Сети нет практически никакой информации о возможности Apache POI создавать такой полезный элемент таблиц Excel, как фильтры значений для столбцов. Ну, раз такой информации нет, попытаемся восполнить этот пробел в наших знаниях :)

Итак, фильтры - это атрибуты столбцов таблицы Excel, позволяющие выбрать из таблицы строки по значению ячеек того или иного столбца. В документе Excel фильтр обычно выглядит как-то так:

Судя по документации, в стабильной версии POI (3.6) поддержки фильтров нет в принципе (видимо, этим и объясняется практически полное отсутсвие информации об этом функционале в Сети). Тем не менее, если внимательно изучить API Javadocs, можно обнаружить там загадочный интерфейс org.apache.poi.ss.usermodel.AutoFilter, а также его не менее загадочные реализации для xls и xlsx - org.apache.poi.hssf.usermodel.HSSFAutoFilter и org.apache.poi.xssf.usermodel.XSSFAutoFilter.

Ввиду того, что в использовавшейся нами стабильной версии POI таких классов и интерфейсов не оказалось, а фильтры в xls-документах были нужны очень сильно, было принято решение попробовать тестовую версию POI - сейчас это 3.7beta3. Как это ни странно, тестовая версия зарекомендовала себя так же хорошо, как и стабильная - никаких проблем в решаемых нами задачах не возникло, зато добавились новые возможности, в числе которых и пресловутые фильтры. Заранее оговорюсь, что создавать конфигурируемые фильтры (т.е. всё то, что не autofilter) POI пока, к сожалению не умеет.

Итак, вот как должен выглядеть код метода, генерирующего показанный на скриншоте выше xls-файл (Person - это POJO-класс наподобие приведённого в предыдущей заметке):

//Заголовки столбцов
private static final String LAST_NAME_COLUMN_HEADER = "Фамилия";
private static final String FIRST_NAME_COLUMN_HEADER = "Имя";
private static final String SECOND_NAME_COLUMN_HEADER = "Отчество";
private static final String ADDRESS_COLUMN_HEADER = "Адрес";
private static final String SPHONE_COLUMN_HEADER = "Телефон";

...

/*
 * Генерируем xls для заданного списка контактов и отдаём его 
 * в байтовый поток записи
 */
private void generateXls(ByteArrayOutputStream out, List<Person> contacts) {
    try {
        HSSFWorkbook wb = new HSSFWorkbook(); //Создаём новый документ
        HSSFSheet sheet = wb.createSheet(); //Создаём страницу

        //Объявляем переменные для строки и ячейки
        HSSFRow row;
        HSSFCell cell;
        
        //Создаём "шапку" таблицы
        row = sheet.createRow(0);
        cell = row.createCell(0, Cell.CELL_TYPE_STRING);
        cell.setCellValue(LAST_NAME_COLUMN_HEADER); //Фамилия
        cell = row.createCell(1, Cell.CELL_TYPE_STRING);
        cell.setCellValue(FIRST_NAME_COLUMN_HEADER); //Имя
        cell = row.createCell(2, Cell.CELL_TYPE_STRING);
        cell.setCellValue(SECOND_NAME_COLUMN_HEADER); //Отчество
        cell = row.createCell(3, Cell.CELL_TYPE_STRING);
        cell.setCellValue(ADDRESS_COLUMN_HEADER); //Адрес
        cell = row.createCell(4, Cell.CELL_TYPE_STRING);
        cell.setCellValue(PHONE_COLUMN_HEADER); //Телефон
        
        if(contacts != null){
            //Создаём фильтры на весь диапазон значений таблицы
            sheet.setAutoFilter(CellRangeAddress.valueOf("A" +
                String.valueOf(1) + ":E" +
                String.valueOf(contacts.size())));
            
            //Записываем содержимое в ячейки
            if(contacts != null && contacts.size() > 0){
                for(int i = 0; i < contacts.size(); i++){
                    Person person = contacts.get(i);
                    int rowIndex = i + 1; //Нулевой столбец - "шапка" таблицы
                    row = sheet.createRow(rowIndex); // Создаём новую строку
                    cell = row.createCell(0, Cell.CELL_TYPE_STRING);
                    cell.setCellValue(person.getLastName()); //Фамилия
                    cell = row.createCell(1, Cell.CELL_TYPE_STRING);
                    cell.setCellValue(person.getFirstName()); //Имя
                    cell = row.createCell(2, Cell.CELL_TYPE_STRING);
                    cell.setCellValue(person.getSecondName()); //Отчество
                    cell = row.createCell(3, Cell.CELL_TYPE_STRING);
                    cell.setCellValue(person.getAddress()); //Адрес
                    cell = row.createCell(4, Cell.CELL_TYPE_STRING);
                    cell.setCellValue(person.getPhone()); //Телефон
                }
            }
        }
            
        wb.write(out);
        out.close();
    } catch (Exception e) {
        LOG.error(e.getMessage(), e);
    }
}

Ключевой элемент листинга - строки 35-37, в которых, собственно, и реализовано добавление фильтра. Фильтр добавляется на диапазон ячеек - т.к. наша таблица содержит 5 столбцов, то столбцы мы берём с "A" по "E" включительно, а количество строк зависит от количества контактов в списке.

Собственно, всё! Приведённый выше код генерирует таблицу с фильтрами - можно пользоваться :)

Коротко о себе:

Работаю ведущим программистом в компании Tune IT и ассистентом кафедры Вычислительной техники в Университете ИТМО .

Занимаюсь проектами, связанными с разработкой разного рода веб-приложений (порталы, CRM-системы, системы электронного документооборота), а также, в рамках научной работы на кафедре, изучаю возможности применения семантического анализа в задачах САПР.