首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >JTable RowFilter是如何工作的?

JTable RowFilter是如何工作的?
EN

Stack Overflow用户
提问于 2015-05-26 01:57:01
回答 2查看 7.9K关注 0票数 10

我试图为JTable创建一个行过滤器,以限制表中显示的行数。

RowFilter代码很简单。它将模型行号转换为视图行号(如果对表进行排序),然后检查视图行号是否小于表中要显示的行数:

代码语言:javascript
运行
复制
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“,输出如下:

代码语言:javascript
运行
复制
Change the Filter to: 1
m0 : v0
m1 : v0
m2 : v0
m3 : v0
m4 : v0

这个输出告诉我,所有模型行都被转换为视图行0。由于0小于过滤器值1,所以所有行都包含在筛选器中(这是错误的)。

因此,这里的问题是为什么convertRowIndexToView(modelRow)不能像预期的那样工作呢?

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

代码语言:javascript
运行
复制
Change the Filter to: 2
m0 : v0
m1 : v1
m2 : v2
m3 : v3
m4 : v4

如您所见,模型行现在正在映射到正确的视图行,因此过滤器中只包含了2行,这是正确的。

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

代码语言:javascript
运行
复制
Change the Filter to: 3
m0 : v0
m1 : v1
m2 : v-1
m3 : v-1
m4 : v-1

在本例中,最后3个模型行被转换为-1,我假设这意味着该行当前在表中不可见,这是正确的。因此,在本例中,所有5行都再次包含在过滤器中,这是不正确的,因为我们只需要前3行。

因此,这里的问题是如何重置过滤器,以便将所有模型行映射到原始视图行?

我试着用:

代码语言:javascript
运行
复制
((TableRowSorter) table.getRowSorter()).setRowFilter(null);
((TableRowSorter) table.getRowSorter()).setRowFilter(filter);

要清除筛选器,在重置过滤器之前,这将给出与步骤1相同的结果,即,现在所有模型行映射为查看行0(因此所有5行仍被显示)。

下面是测试代码:

代码语言:javascript
运行
复制
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“行吗?

哦,是的,还有一点令人沮丧。如果你取消对两个System.out的注释。在actionPeformed()方法中,当您从组合框中选择1时,您会注意到在这两种情况下,模型索引4被转换为视图索引4,这两个输出被夹在不正确的模型周围以查看转换?

编辑:

基于MadProgrammers的建议,我尝试:

代码语言:javascript
运行
复制
//((TableRowSorter) table.getRowSorter()).setRowFilter(filter);
TableRowSorter sorter = new TableRowSorter();
table.setRowSorter( sorter );
sorter.setRowFilter( filter );
sorter.sort();

现在我在桌子上什么都没有。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-05-26 04:49:55

使用@MadProgrammer的提示,我想出了以下解决方案。

不仅需要替换RowSorter,还需要保留排序键,以便sort()方法将表重置回当前的排序状态:

代码语言:javascript
运行
复制
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();
            }
        });
    }
}
票数 4
EN

Stack Overflow用户

发布于 2015-05-26 05:04:18

因此,经过一些认真的测试和调试,我将DefaultRowSorterTableRowSorter的代码复制到您的FilterSSCCE中,并添加了一些监视modelToView字段的输出,这是DefaultRowSorter#convertRowIndexToView用来在模型和视图之间映射的.

代码语言:javascript
运行
复制
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: 1Change the Filter to: 2之间。您可以看到,initializeFilteredMapping已经将超出范围的模型索引设置为-1,但是当我们更改为Change the Filter to: 2时,仍然设置了相同的索引,更改过滤器并没有重置它们。

这似乎是一个保持表响应性的设计选择,而且他们可能从未想过有人会尝试从过滤器中访问视图,因为您应该使用模型数据.

怎么绕过它.?

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

您可以编写一个“代理”TableModel,它“知道”JTable的排序状态(可能通过RowSorter),它可以充当筛选器来确定可见的行数,但随着模型开始冒险进入视图的世界,它正进入模糊的水中……

另一个选择是改变setFilter方法的工作方式,重置modelToViewviewToModel变量,但它们是private,正如它们应该的那样,好吧,我们可以使用DefaultRowSorter中可用的createModelToViewcreateViewToModelsetModelToViewFromViewToModel方法。但他们是private .

对这些变量进行认真修改的任何有用的方法似乎都是我生命中的private...story .(拿上你的手电筒和干草叉,我们正在进行一次电子搜索)

下一个选择,写它所有的yourself...what一个美妙的想法,期望这违背了OO的基本原则.

一个“工作”(我使用的术语非常,非常轻),将是使用反射和调用我们需要的方法.

代码语言:javascript
运行
复制
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

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30448255

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档