I currently have a JTable that is populated with a series of data that forms the basis of a import screen. When I have finished selecting which updates I want or do not want, I press on the Apply button and the updates are applied successfully but the JTable does not fully update.
This is the code for the method that deals with applying the changes:
private void doProcessChanges() {
ChangeProcessor cp = new ChangeProcessor();
final List<Integer> rowsToRemove = new ArrayList<Integer>();
BeanTableModel<UpdateModel> model = (BeanTableModel<UpdateModel>) table.getModel();
for (int i=0; i<model.getRowCount(); i++) {
UpdateRow ur = mode.getObject(i);
if (ur.isAccepted() <> ChangeAcceptance.NO_ACTION) {
cp.processChange(ur);
rowsToRemove.add(i);
}
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
for (int row : rowsToRemove) {
model.removeObject(row);
model.fireTableDataChanged();
}
}
);
}
The method is called from within a SwingWorker thread as below:
SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
@Override
protected Object doInBackground() throws Exception {
doProcessChanges();
return null;
}
@Override
protected void done() {
try {
get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
I do not get any exceptions from executing this so I am I doing anything wrong? Thanks in advance.
Your fragment shows incorrect synchronization. In particular, you access BeanTableModel
, a subclass of AbstractTableModel
, from the background thread. Instead, pass the List<Integer> rowsToRemove
to your worker in its constructor.
Addendum: Instead of invokeLater()
, you can update the TableModel
in your implementation of process()
, which executes on the EDT. Also, you shouldn't have to fireTableDataChanged()
, which "Notifies all listeners that all cell values in the table's rows may have changed." The removeObject()
implementation should fire the least pervasive event required to effect the change.