Java 行过滤器不使用';无法在单元格更新事件上按预期工作
在一个共享表模型示例中,我意识到如果我们将一个行过滤器附加到表的行分类器上,这个过滤器对单元格更新事件没有任何影响。根据:Java 行过滤器不使用';无法在单元格更新事件上按预期工作,java,swing,jtable,rowfilter,rowsorter,Java,Swing,Jtable,Rowfilter,Rowsorter,在一个共享表模型示例中,我意识到如果我们将一个行过滤器附加到表的行分类器上,这个过滤器对单元格更新事件没有任何影响。根据: RowSorter的具体实现需要参考这样的模型 作为TableModel或ListModel。视图类,例如JTable和 JList,也将有对该型号的参考。避免订购 依赖项,RowSorter实现不应安装侦听器 在模型上。相反,当 模式改变了。例如,如果在TableModel JTable调用rowsUpdated。当模型更改时,视图可能会调用 转换为以下任一方法:mode
RowSorter
的具体实现需要参考这样的模型
作为TableModel
或ListModel
。视图类,例如JTable
和
JList
,也将有对该型号的参考。避免订购
依赖项,RowSorter
实现不应安装侦听器
在模型上。相反,当
模式改变了。例如,如果在TableModel
JTable
调用rowsUpdated
。当模型更改时,视图可能会调用
转换为以下任一方法:modelStructureChanged
,
所有行已更改
,行已插入
,行已删除
和行已更新
因此,根据我对本段的理解,单元格更新是行更新的一种特殊情况,因此应调用rowsUpdated
,并相应地对行进行过滤
为了说明我所说的,请考虑这个简单的过滤器:
private void applyFilter() {
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(new RowFilter() {
@Override
public boolean include(RowFilter.Entry entry) {
Boolean value = (Boolean)entry.getValue(2);
return value == null || value;
}
});
}
此处,第三列应为布尔值
,如果单元格值为null
或true
,则必须包含条目
(行)。如果我编辑一个位于第三列的单元格,并将其值设置为false
,那么我希望这一行从视图中“消失”。然而,为了实现这一点,我必须再次设置一个新的过滤器,因为它似乎不会“自动”工作
将TableModelListener
附加到模型,如下所示,我可以看到单元格编辑的更新事件:
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
int row = e.getLastRow();
int column = e.getColumn();
Object value = ((TableModel)e.getSource()).getValueAt(row, column);
String text = String.format("Update event. Row: %1s Column: %2s Value: %3s", row, column, value);
System.out.println(text);
}
}
});
正如我所说,如果我使用这个TableModelListener
重置过滤器,那么它将按预期工作:
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
applyFilter();
}
}
});
问题:这是一个bug/实现问题吗?或者我误解了API
下面是一个完整的例子来说明这个问题
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.DefaultRowSorter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class Demo {
private JTable table;
private void createAndShowGUI() {
DefaultTableModel model = new DefaultTableModel(5, 3) {
@Override
public boolean isCellEditable(int row, int column) {
return column == 2;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 2 ? Boolean.class : super.getColumnClass(columnIndex);
}
};
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE) {
int row = e.getLastRow();
int column = e.getColumn();
Object value = ((TableModel)e.getSource()).getValueAt(row, column);
String text = String.format("Update event. Row: %1s Column: %2s Value: %3s", row, column, value);
System.out.println(text);
// applyFilter(); un-comment this line to make it work
}
}
});
table = new JTable(model);
table.setAutoCreateRowSorter(true);
applyFilter();
JPanel content = new JPanel(new BorderLayout());
content.setBorder(BorderFactory.createEmptyBorder(8,8,8,8));
content.add(new JScrollPane(table));
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(content);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private void applyFilter() {
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(new RowFilter() {
@Override
public boolean include(RowFilter.Entry entry) {
Boolean value = (Boolean)entry.getValue(2);
return value == null || value;
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Demo().createAndShowGUI();
}
});
}
}
导入java.awt.BorderLayout;
导入javax.swing.BorderFactory;
导入javax.swing.DefaultRowSorter;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JScrollPane;
导入javax.swing.JTable;
导入javax.swing.RowFilter;
导入javax.swing.SwingUtilities;
导入javax.swing.event.TableModelEvent;
导入javax.swing.event.TableModelListener;
导入javax.swing.table.DefaultTableModel;
导入javax.swing.table.TableModel;
公开课演示{
专用JTable表;
私有void createAndShowGUI(){
DefaultTableModel=新的DefaultTableModel(5,3){
@凌驾
公共布尔值可编辑(int行,int列){
返回列==2;
}
@凌驾
公共类getColumnClass(int columnIndex){
返回columnIndex==2?Boolean.class:super.getColumnClass(columnIndex);
}
};
addTableModelListener(新的TableModelListener(){
@凌驾
公共作废表已更改(TableModelEvent e){
if(e.getType()==TableModelEvent.UPDATE){
int row=e.getLastRow();
int column=e.getColumn();
对象值=((TableModel)e.getSource()).getValueAt(行、列);
String text=String.format(“更新事件。行:%1s列:%2s值:%3s”,行、列、值);
System.out.println(文本);
//applyFilter();取消对此行的注释以使其正常工作
}
}
});
表=新JTable(型号);
表.setAutoCreateRowSorter(真);
applyFilter();
JPanel content=newjpanel(newborderlayout());
content.setBorder(BorderFactory.createEmptyByOrder(8,8,8,8));
添加(新的JScrollPane(表));
JFrame=新JFrame(“演示”);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
框架。添加(内容);
frame.pack();
frame.setLocationByPlatform(真);
frame.setVisible(true);
}
私有void applyFilter(){
DefaultRowSorter-sorter=(DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(新的RowFilter(){
@凌驾
公共布尔包含(RowFilter.Entry){
Boolean value=(Boolean)entry.getValue(2);
返回值==null | |值;
}
});
}
公共静态void main(字符串[]args){
SwingUtilities.invokeLater(新的Runnable(){
@凌驾
公开募捐{
新建演示().createAndShowGUI();
}
});
}
}
好吧,在做了一些研究并阅读了API、bug报告和Oracle论坛之后,我发现了一些有趣的东西
1.将DefaultRowSorter的sortsOnUpdate属性设置为true
我发现的第一件事是我们必须将属性设置为true
,以便在调用时启用通知链。否则将触发no,并且视图(我们的JTable)不会意识到发生了什么,也不会相应地重新绘制。所以做一点小小的改变:
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
sorter.setRowFilter(new RowFilter() {
@Override
public boolean include(RowFilter.Entry entry) {
Boolean value = (Boolean)entry.getValue(2);
return value == null || value;
}
});
sorter.setSortsOnUpdates(true);
我们不必在表模型更新上重新应用过滤器。但是
2.处理RowsortRevent通知的JTable组件中存在错误
当JTable实现RowSorterListener
接口时,将自身订阅到Rowsorter作为侦听器并处理RowsorterListents
。有一只虫子在重新粉刷桌子。这些帖子很好地描述了这种奇怪的行为:
rowsortrevent.TYPE.SORTED
事件时,它只重新绘制与非折叠行相关的区域,而不重新绘制表的其余部分,该部分保留
DefaultRowSorter sorter = (DefaultRowSorter)table.getRowSorter();
...
sorter.addRowSorterListener(new RowSorterListener() {
@Override
public void sorterChanged(RowSorterEvent e) {
if (e.getType() == RowSorterEvent.Type.SORTED) {
// We need to call both revalidate() and repaint()
table.revalidate();
table.repaint();
}
}
});
JTable table = new JTable(tableModel) {
@Override
public void sorterChanged(RowSorterEvent e) {
super.sorterChanged(e);
if (e.getType() == RowSorterEvent.Type.SORTED) {
resizeAndRepaint(); // this protected method calls both revalidate() and repaint()
}
}
};
//----> start hack around core issue 6791934:
// table not updated correctly after updating model
// while having a sorter with filter.
/**
* Overridden to hack around core bug
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791934
*
*/
@Override
public void sorterChanged(RowSorterEvent e) {
super.sorterChanged(e);
postprocessSorterChanged(e);
}
/** flag to indicate if forced revalidate is needed. */
protected boolean forceRevalidate;
/** flag to indicate if a sortOrderChanged has happened between pre- and postProcessModelChange. */
protected boolean filteredRowCountChanged;
/**
* Hack around core issue 6791934: sets flags to force revalidate if appropriate.
* Called before processing the event.
* @param e the TableModelEvent received from the model
*/
protected void preprocessModelChange(TableModelEvent e) {
forceRevalidate = getSortsOnUpdates() && getRowFilter() != null && isUpdate(e) ;
}
/**
* Hack around core issue 6791934: forces a revalidate if appropriate and resets
* internal flags.
* Called after processing the event.
* @param e the TableModelEvent received from the model
*/
protected void postprocessModelChange(TableModelEvent e) {
if (forceRevalidate && filteredRowCountChanged) {
resizeAndRepaint();
}
filteredRowCountChanged = false;
forceRevalidate = false;
}
/**
* Hack around core issue 6791934: sets the sorter changed flag if appropriate.
* Called after processing the event.
* @param e the sorter event received from the sorter
*/
protected void postprocessSorterChanged(RowSorterEvent e) {
filteredRowCountChanged = false;
if (forceRevalidate && e.getType() == RowSorterEvent.Type.SORTED) {
filteredRowCountChanged = e.getPreviousRowCount() != getRowCount();
}
}
//----> end hack around core issue 6791934: