Java JTable RowFilter是如何工作的?

Java JTable RowFilter是如何工作的?,java,swing,jtable,rowfilter,Java,Swing,Jtable,Rowfilter,我试图为JTable创建一个行过滤器,以限制表中显示的行数 RowFilter代码很简单。它将模型行号转换为视图行号(如果表格已排序),然后检查视图行号是否小于要在表格中显示的行数: RowFilter<TableModel, Integer> filter = new RowFilter<TableModel, Integer>() { @Override public boolean include(RowFilter.Entry<? exten

我试图为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;
    }

};
此输出告诉我所有模型行都将转换为视图行0。由于0小于筛选器值1,因此所有行都包含在筛选器中(这是错误的)

所以这里的问题是为什么
convertRowIndexToView(modelRow)
不能按预期工作

2) 现在从组合框中选择“2”,您将得到如下输出:

Change the Filter to: 1
m0 : v0
m1 : v0
m2 : v0
m3 : v0
m4 : v0
Change the Filter to: 2
m0 : v0
m1 : v1
m2 : v2
m3 : v3
m4 : v4
Change the Filter to: 3
m0 : v0
m1 : v1
m2 : v-1
m3 : v-1
m4 : v-1
如您所见,模型行现在映射到正确的视图行,因此过滤器中只包含2行,这是正确的

3) 现在从组合框中选择“3”,您将得到如下输出:

Change the Filter to: 1
m0 : v0
m1 : v0
m2 : v0
m3 : v0
m4 : v0
Change the Filter to: 2
m0 : v0
m1 : v1
m2 : v2
m3 : v3
m4 : v4
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();
            }
        });
    }
}

现在我什么都没有了。

根据@MadProgrammer的提示,我想出了以下解决方案

不仅需要更换行排序器,还需要保留排序键,以便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();
            }
        });
    }
}
import java.awt.*;
导入java.awt.event.*;
导入javax.swing.*;
导入javax.swing.table.*;
公共类筛选器SCCE扩展了JPanel
{
专用JTable表;
公共过滤器scce()
{
setLayout(新的BorderLayout());
JComboBox comboBox=新的JComboBox();
comboBox.addItem(新整数(1));
comboBox.addItem(新整数(2));
comboBox.addItem(新整数(3));
comboBox.addItem(新整数(4));
comboBox.addItem(新整数(5));
组合框。设置所选索引(4);
comboBox.addActionListener(新ActionListener()
{
@凌驾
已执行的公共无效操作(操作事件e)
{
整数值=(整数)组合框。getSelectedItem();
新过滤器(值);
}
});
添加(组合框,BorderLayout.NORTH);
表=新的JTable(5,1);
对于(int i=0;i公共布尔包含(RowFilter.Entry因此,经过一些认真的测试和调试,我将
DefaultRowSorter
TableRowSorter
的代码复制到
filterssce
中,并添加了一些输出监控
modelToView
字段,该字段由
DefaultRowSorter>convertRowIndexToView
用于在模型和d视图指示

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]
有趣的部分在最后,在
将过滤器更改为:1
将过滤器更改为:2
之间。您可以看到
initializeFilteredMapping
已将超出范围的模型索引设置为
-1
,但当我们更改为
将过滤器更改为:2
时,仍然设置了相同的索引,cha检查过滤器是否未重置它们

这似乎是一种保持表响应性的设计选择,他们可能从来没有想到有人会尝试从过滤器中访问视图,因为假设您正在使用模型数据

如何绕过它

您可以构建一个“代理”
TableModel
,但这排除了对表进行排序的可能性

您可以编写一个“代理”
TableModel
,它“知道”关于
JTable
的排序状态(可能通过
RowSorter
),它可以作为过滤器来确定可见的行数,但随着模型开始进入视图世界,这一点正在变得模糊

另一种选择是更改
setFilter
方法的工作方式,并重置
modelToView
viewToModel
变量,但它们是
private
,我们可以使用
createModelToView
createViewToModel
setModelToViewFromViewToModel方法可在
DefaultRowSorter
中使用,但它们是
专用的

似乎任何处理这些变量严重修改的有用方法都是
私有的
…我的生活故事…(拿上你的手电筒和干草叉,我们正在进行开发人员搜寻)

下一个选择,你自己写……这是一个多么美妙的想法,期望它违背OO的基本原则

一个“变通”的方法(我使用这个术语非常非常轻松),就是使用反射,然后调用我们需要的方法

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);
    }

}
公共类TestRowSorter扩展了TableRowSorter{
公共TestRowSorter(){
}
公共测试行分拣机(M型){
超级(模型);
}
公共方法findMethod(字符串名称、类…类型){
返回findMethod(getClass(),name,lstTypes);
}
公共方法findMethod(类父类、字符串名称、类…类型){
方法=null;
试一试{
方法=parent.getDeclaredMet