Как работает Jtable RowFilter?

Я пытаюсь создать фильтр строк для JTable, чтобы ограничить количество строк, отображаемых в таблице.

код RowFilter прост. Он преобразует номер строки модели в номер строки представления (в случае сортировки таблицы), а затем проверяет, меньше ли номер строки представления, чем количество строк, отображаемых в таблице:

RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>()
{
    @Override
    public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
    {
        int modelRow = entry.getIdentifier();
        int viewRow = table.convertRowIndexToView(modelRow);

        return viewRow < numberOfRows;
    }

};

проблема в том, что номер строки модели не всегда преобразуется в разумный номер строки представления, поэтому слишком много строк включено в фильтр. Для демонстрации запустите код ниже:

1) Выберите " 1 " из поля со списком, и вы получите вывод, как:

Change the Filter to: 1
m0 : v0
m1 : v0
m2 : v0
m3 : v0
m4 : v0

этот вывод говорит мне, что все строки модели преобразуются в строку просмотра 0. Поскольку 0 меньше значения фильтра 1, все строки включаются в фильтр (что неверно).

Итак, вопрос в том, почему convertRowIndexToView(modelRow) не работает, как ожидалось?

2) Теперь выберите "2" в поле со списком, и вы будете получить вывод, как:

Change the Filter to: 2
m0 : v0
m1 : v1
m2 : v2
m3 : v3
m4 : v4

как вы можете видеть, строки модели теперь сопоставляются с правильной строкой представления,поэтому в фильтр включены только 2 строки.

3) Теперь выберите " 3 " из поля со списком, и вы получите вывод, как:

Change the Filter to: 3
m0 : v0
m1 : v1
m2 : v-1
m3 : v-1
m4 : v-1

в этом случае последние 3 строки модели преобразуются в -1, что, как я предполагаю, означает, что строка в настоящее время не отображается в таблице, что является правильным. Таким образом, в этом случае все 5 строк снова включены в фильтр, который неверно, так как мы хотим только первые 3.

Итак, вопрос в том, как сбросить фильтр, чтобы все строки модели были сопоставлены с исходной строкой представления?

Я пытался использовать:

((TableRowSorter) table.getRowSorter()).setRowFilter(null);
((TableRowSorter) table.getRowSorter()).setRowFilter(filter);

чтобы очистить фильтр, перед сбросом фильтра, но это дало те же результаты, что и Шаг 1. То есть теперь все строки модели сопоставляются для просмотра строки 0 (поэтому все 5 строк по-прежнему отображаются).

вот тестовый код:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class FilterSSCCE extends JPanel
{
    private JTable table;

    public FilterSSCCE()
    {
        setLayout( new BorderLayout() );

        JComboBox<Integer> comboBox = new JComboBox<Integer>();
        comboBox.addItem( new Integer(1) );
        comboBox.addItem( new Integer(2) );
        comboBox.addItem( new Integer(3) );
        comboBox.addItem( new Integer(4) );
        comboBox.addItem( new Integer(5) );
        comboBox.setSelectedIndex(4);

        comboBox.addActionListener( new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                //System.out.println( table.convertRowIndexToView(4) );
                Integer value = (Integer)comboBox.getSelectedItem();
                newFilter( value );
                //System.out.println( table.convertRowIndexToView(4) );
            }
        });
        add(comboBox, BorderLayout.NORTH);

        table = new JTable(5, 1);

        for (int i = 0; i < table.getRowCount(); i++)
            table.setValueAt(String.valueOf(i+1), i, 0);

        table.setAutoCreateRowSorter(true);
        JScrollPane scrollPane = new JScrollPane(table);
        add(scrollPane, BorderLayout.CENTER);
    }

    private void newFilter(int numberOfRows)
    {
        System.out.println("Change the Filter to: " + numberOfRows);

        RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>()
        {
            @Override
            public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
            {
                int modelRow = entry.getIdentifier();
                int viewRow = table.convertRowIndexToView(modelRow);

                System.out.println("m" + modelRow + " : v" + viewRow);

                return viewRow < numberOfRows;
            }

        };

        ((TableRowSorter) table.getRowSorter()).setRowFilter(filter);
    }

    private static void createAndShowGUI()
    {
        JPanel panel = new JPanel();

        JFrame frame = new JFrame("FilterSSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new FilterSSCCE());
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}

любая идея, как создать строку фильтр для отображения первых" n " строк?

Ах да, еще один разочаровывающий момент. Если вы раскомментируете две системы.из.. строки в методе actionPeformed () при выборе 1 из поля со списком вы заметите, что в обоих случаях индекс модели 4 преобразуется в индекс просмотра 4, и эти два выхода зажаты вокруг неправильной модели для просмотра преобразований???

Edit:

основываясь на предложении MadProgrammers, я попытался:

//((TableRowSorter) table.getRowSorter()).setRowFilter(filter);
TableRowSorter sorter = new TableRowSorter();
table.setRowSorter( sorter );
sorter.setRowFilter( filter );
sorter.sort();

теперь я получаю в столе ничего нет.

2 ответов


Итак, после серьезного тестирования и отладки, я скопировал код DefaultRowSorter и TableRowSorter в своем FilterSSCCE и добавил некоторый мониторинг вывода modelToView поле, которое используется DefaultRowSorter#convertRowIndexToView для отображения между моделью и просмотра показателей...

Change the Filter to: 1
createModelToView = [0, 0, 0, 0, 0]
m0 : v0
m1 : v0
m2 : v0
m3 : v0
m4 : v0
initializeFilteredMapping.1 = [0, 1, 2, 3, 4]
initializeFilteredMapping.2 = [0, 1, 2, 3, 4]
Change the Filter to: 5
m0 : v0
m1 : v1
m2 : v2
m3 : v3
m4 : v4
initializeFilteredMapping.1 = [0, 1, 2, 3, 4]
initializeFilteredMapping.2 = [0, 1, 2, 3, 4]
Change the Filter to: 1
m0 : v0
m1 : v1
m2 : v2
m3 : v3
m4 : v4
initializeFilteredMapping.1 = [0, -1, -1, -1, -1]
initializeFilteredMapping.2 = [0, -1, -1, -1, -1]
Change the Filter to: 2
m0 : v0
m1 : v-1
m2 : v-1
m3 : v-1
m4 : v-1
initializeFilteredMapping.1 = [0, 1, 2, 3, 4]
initializeFilteredMapping.2 = [0, 1, 2, 3, 4]

самое интересное здесь, в конце, между Change the Filter to: 1 и Change the Filter to: 2. Вы можете видеть это initializeFilteredMapping установил индексы модели, которые находятся вне диапазона -1, но когда мы переходим к Change the Filter to: 2 те же показатели по-прежнему, изменение фильтра не сбросило их.

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

как обойти это...?

вы можете создать "прокси"TableModel, но это исключает возможность сортировки таблицы.

вы могли бы написать "прокси" TableModel который "знал" о отсортированном состоянии JTable (вероятно, через RowSorter) и который может выступать в качестве фильтра для определения количества видимых строк, но это переходит в мутную воду, поскольку модель начинает рисковать в мир представления...

другим выбором было бы изменить способ, которым setFilter метод работает и сбросить modelToView и viewToModel переменные, но они являются private, как и должно быть, хорошо, мы могли бы использовать createModelToView, createViewToModel и setModelToViewFromViewToModel методы доступно в DefaultRowSorter ... но они private to...

казалось бы, почти любой полезный метод, который имеет дело с серьезной модификацией этих переменных, -private...история моей жизни...(берите свои факелы и вилы, мы отправляемся на охоту)

следующий выбор, напишите все сами...какая замечательная идея, ожидайте, что это идет вразрез с основными принципами ОО...

"работа вокруг" (и я использую термин очень, очень легко), было бы используйте рефлексию и просто вызывайте методы, которые нам нужны...

public class TestRowSorter<M extends TableModel> extends TableRowSorter<M> {

    public TestRowSorter() {
    }

    public TestRowSorter(M model) {
        super(model);
    }

    public Method findMethod(String name, Class... lstTypes) {

        return findMethod(getClass(), name, lstTypes);

    }

    public Method findMethod(Class parent, String name, Class... lstTypes) {

        Method method = null;
        try {
            method = parent.getDeclaredMethod(name, lstTypes);
        } catch (NoSuchMethodException noSuchMethodException) {
            try {
                method = parent.getMethod(name, lstTypes);
            } catch (NoSuchMethodException nsm) {
                if (parent.getSuperclass() != null) {
                    method = findMethod(parent.getSuperclass(), name, lstTypes);
                }
            }
        }

        return method;

    }

    @Override
    public void setRowFilter(RowFilter<? super M, ? super Integer> filter) {

        try {
            Method method = findMethod("createModelToView", int.class);
            method.setAccessible(true);
            method.invoke(this, getModelWrapper().getRowCount());

            method = findMethod("createViewToModel", int.class);
            method.setAccessible(true);
            method.invoke(this, getModelWrapper().getRowCount());

            method = findMethod("setModelToViewFromViewToModel", boolean.class);
            method.setAccessible(true);
            method.invoke(this, true);
        } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exp) {
            exp.printStackTrace();
        }

        super.setRowFilter(filter);
    }

}

Итак, ответ, не получить доступ к представлению из фильтра.

вообще говоря, я склонен заменить RowSorter когда - либо я заменяю RowFilter как он избегает такого рода вопросы: P


используя советы от @MadProgrammer, я придумал следующее решение.

мало того, что нужно заменить RowSorter, вам также нужно сохранить ключи сортировки, чтобы метод sort () сбросил таблицу до ее текущего состояния сортировки:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class FilterSSCCE extends JPanel
{
    private JTable table;

    public FilterSSCCE()
    {
        setLayout( new BorderLayout() );

        JComboBox<Integer> comboBox = new JComboBox<Integer>();
        comboBox.addItem( new Integer(1) );
        comboBox.addItem( new Integer(2) );
        comboBox.addItem( new Integer(3) );
        comboBox.addItem( new Integer(4) );
        comboBox.addItem( new Integer(5) );
        comboBox.setSelectedIndex(4);

        comboBox.addActionListener( new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                Integer value = (Integer)comboBox.getSelectedItem();
                newFilter( value );
            }
        });
        add(comboBox, BorderLayout.NORTH);

        table = new JTable(5, 1);

        for (int i = 0; i < table.getRowCount(); i++)
            table.setValueAt(String.valueOf(i+1), i, 0);

        table.setAutoCreateRowSorter(true);
        JScrollPane scrollPane = new JScrollPane(table);
        add(scrollPane, BorderLayout.CENTER);
    }

    private void newFilter(int numberOfRows)
    {
        RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>()
        {
            @Override
            public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry)
            {
                int modelRow = entry.getIdentifier();
                int viewRow = table.convertRowIndexToView(modelRow);

                return viewRow < numberOfRows;
            }

        };

        TableRowSorter oldSorter = (TableRowSorter)table.getRowSorter();
        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
        table.setRowSorter( sorter );
        sorter.setRowFilter( filter );
        sorter.setSortKeys( oldSorter.getSortKeys() );
        sorter.sort();
    }

    private static void createAndShowGUI()
    {
        JPanel panel = new JPanel();

        JFrame frame = new JFrame("FilterSSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new FilterSSCCE());
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}