The situation is this: I have a table model in which some of the columns can take their lazy ol' time initializing so they are only loaded as needed. I also wanted to sort on these columns. Of course, the problem is when the sort is started, it only does a partial sort as some of the values aren't actually initialized yet. So setSortsOnUpdates to the rescue!
Well not really. It will sort, but the view isn't repainted properly (or perhaps not at all).
In my context, I am calling repaint on the bounds of a table row after it is loaded and that seems to fix the problem. It was initially broken when I tried to retrofit the cache mechanism to be able to handle the situation - it was calling repaint on the wrong row which is how I stumbled with this problem in the first place.
But it doesn't seem like it should be enough. All the rows that are shifted ought to need repainted too, no?
Anyway, here is a simulation of the situation which depicts the repaint problem. Uncomment the four statements in the actionPerformed method to see that repainting the single row seems to work, at least for this specific circumstance. Sort on the second column, wait about 5 seconds, click around.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.RowSorter.SortKey;
import javax.swing.event.*;
import javax.swing.table.*;
public class SortTest implements Runnable, RowSorterListener, ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new SortTest());
}
@Override
public void run() {
model = new DefaultTableModel(100, 2) {
public Class<?> getColumnClass(int column) {
return column == 1 ? Integer.class : Object.class;
}
};
for (int row=model.getRowCount(); --row>=0;) {
model.setValueAt(createString(), row, 0);
model.setValueAt(0, row, 1);
}
table = new JTable(model);
table.setAutoCreateRowSorter(true);
TableRowSorter<?> sorter = (TableRowSorter<?>)table.getRowSorter();
sorter.setSortsOnUpdates(true);
sorter.addRowSorterListener(this);
JFrame frame = new JFrame(getClass().getSimpleName());
frame.add(new JScrollPane(table));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JTable table;
private DefaultTableModel model;
@Override
public void sorterChanged(RowSorterEvent e) {
if (load && e.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) {
List<SortKey> keys = e.getSource().getSortKeys();
for (SortKey key : keys) {
if (key.getColumn() == 1) {
load = false;
new Timer(20, this).start();
break;
}
}
}
}
private static Random random = new Random();
private boolean load = true;
private int row;
@Override
public void actionPerformed(ActionEvent e) {
model.setValueAt(new Integer(random.nextInt(900)+100), row, 1);
// Rectangle bounds = table.getCellRect(
// table.convertRowIndexToView(row), 0, false);
// bounds.x = 0;
// bounds.width = table.getWidth();
// table.repaint(bounds);
if (++row == model.getRowCount())
((Timer)e.getSource()).stop();
}
private static String createString() {
int len = random.nextInt(5)+3;
char[] c = new char[len];
for (int j=c.length; --j>=0;)
c[j] = (char)('A'+random.nextInt(26));
return new String(c);
}
}