Java JTable RowFilter是如何工作的?
我试图为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
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