I have a JTable that is sorted and filtered using a RowSorter and updates in a real time manner. I have an issue when users are trying to create a selection interval with either the mouse (click + drag over rows) or via the keyboard (SHIFT + arrow down multiple times).
The selection interval start and end get corrupted as soon as a table-model update event occurs. I have discovered that if a table is sorted and there is more than one selected row the selection model fires multiple list selection events that clear then recreate the selection on every table model update.
If multiple rows are selected one at a time - all is well. What the user selected remain selected. If however the user tries to create a selection-interval by clicking and dragging the mouse down over multiple rows - the rows get selected correctly until a table model update event fires. The selection then starts building from the currently selected row.
I've attached a small demo application. Try selecting multiple rows using the mouse - every second the selection will get corrupted....
This seems to be a bug (or unwanted behavior) in the Java classes. I'm hoping someone can provide a creative solution/work-around.
public class TableTest extends JFrame {
private static final long serialVersionUID = 1L;
public TableTest() {
super("Table Selection Problem");
setBounds(50, 50, 800, 600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Create a table model
final DefaultTableModel tableModel = new DefaultTableModel(40,10);
// Create table row sorter sorted on column 1
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
sorter.setSortsOnUpdates(true);
sorter.setSortKeys(Arrays.asList(new RowSorter.SortKey(1,SortOrder.DESCENDING)));
// Create a table and bind the sorter
JTable table = new JTable(tableModel);
table.setRowSorter(sorter);
// Bind a list selection listener, this doesn't do anything besides showing the selection change(s) on every table model update
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
System.out.println(e);
}
});
// place the table in the frame
getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
// Simulate some application events that trigger a table update...
new Thread() {
public void run() {
try {
final Random r = new Random();
for(int x = 0; x < 1000; x++) {
// update a table cell (not even an add or delete)
SwingUtilities.invokeLater(new Runnable() {
public void run() {
tableModel.setValueAt(r.nextInt(1000), r.nextInt(40), r.nextInt(10));
}
});
// wait 1 second before next table update
Thread.sleep(1000);
}
} catch(Throwable t) {
t.printStackTrace();
}
}
}.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
(new TableTest()).setVisible(true);
}
});
}
}