Working in the example with the general model table, I realized that if we attach a row filter to the table row sorter, this filter does not have any effect on the cell update events. According to the RowSorter API :
Specific RowSorter implementations must reference a model such as TableModel or ListModel . View classes such as JTable and JList will also have a model reference. To avoid dependency ordering, the RowSorter implementation should not set the listener on the model. Instead, the view class will call RowSorter when the model changes. For example, if a row is updated in a TableModel JTable calls rowsUpdated . When a model changes, the view can call in any of the following ways: modelStructureChanged , allRowsChanged , rowsInserted , rowsDeleted and rowsUpdated .
So, I understand this paragraph, updating a cell is a special case of updating a row, and as such rowsUpdated should be called and the rowsUpdated should be filtered accordingly.
To illustrate what I'm saying, consider this simple filter:
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; } }); }
Here it is expected that the third column will be Boolean and entry (row) if the cell value is either null or true . If I edit a cell placed in the third column and set its value to false , then I expect this row to simply โdisappearโ from the view. However, for this I need to install a new filter again, because it does not work โautomaticallyโ.
TableModelListener to the model as follows, I can see the update event by changes in the cell:
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); } } });
As I said, if I reset the filter using this TableModelListener , then it works as expected:
model.addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { if (e.getType() == TableModelEvent.UPDATE) { applyFilter(); } } });
Question: - a problem with an error / implementation? Or do I not understand the API?
The following is a complete MCVE problem.
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);