I am having a slight problem with a GUI where the ProgressCellRenderer is not updating until I either click on it or highlight it or even resize the window. I have slimmed down the code to just use generic data and using sleeps to emulate a long running task.
What have I missed that is causing it to not update until I cause an update on the gui itself?
This is minimum working code that will run.
public class Test
{
public static void main(String[] args)
{
new Test();
}
public Test()
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex)
{
}
UpdatableTableModel model = new UpdatableTableModel();
JTable table = new JTable();
table.setModel(model);
table.getColumn("Status").setCellRenderer(new ProgressCellRender());
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
StringFinderWorker worker = new StringFinderWorker(model);
worker.execute();
}
});
}
public class ProgressCellRender extends JProgressBar implements TableCellRenderer
{
private static final long serialVersionUID = 1L;
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
int progress = 0;
if (value instanceof Float)
{
progress = Math.round(((Float) value) * 100f);
}
else if (value instanceof Integer)
{
progress = (int) value;
}
setValue(progress);
return this;
}
}
public class RowData
{
private String string;
private float status;
public RowData(String string)
{
this.string = string;
this.status = 0f;
}
public String getString()
{
return string;
}
public float getStatus()
{
return status;
}
public void setStatus(float status)
{
this.status = status;
}
}
public class UpdatableTableModel extends AbstractTableModel
{
private static final long serialVersionUID = 1L;
private List<RowData> rows;
private Map<String, RowData> mapLookup;
public UpdatableTableModel()
{
rows = new ArrayList<>(25);
mapLookup = new HashMap<>(25);
}
@Override
public int getRowCount()
{
return rows.size();
}
@Override
public int getColumnCount()
{
return 2;
}
@Override
public String getColumnName(int column)
{
String name = "??";
switch (column)
{
case 0:
name = "The String";
break;
case 1:
name = "Status";
break;
}
return name;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
RowData rowData = rows.get(rowIndex);
Object value = null;
switch (columnIndex)
{
case 0:
value = rowData.getString();
break;
case 1:
value = rowData.getStatus();
break;
}
return value;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
RowData rowData = rows.get(rowIndex);
switch (columnIndex)
{
case 3:
if (aValue instanceof Float)
{
rowData.setStatus((float) aValue);
}
break;
}
}
public void addString(String string)
{
RowData rowData = new RowData(string);
mapLookup.put(string, rowData);
rows.add(rowData);
fireTableRowsInserted(rows.size() - 1, rows.size() - 1);
}
protected void updateStatus(String string, int progress)
{
RowData rowData = mapLookup.get(string);
if (rowData != null)
{
int row = rows.indexOf(rowData);
float p = (float) progress / 100f;
setValueAt(p, row, 3);
fireTableCellUpdated(row, 3);
}
}
}
public class StringFinderWorker extends SwingWorker<List<String>, String>
{
private UpdatableTableModel model;
public StringFinderWorker(UpdatableTableModel model)
{
this.model = model;
}
@Override
protected void process(List<String> chunks)
{
for (String string : chunks)
{
model.addString(string);
}
}
protected ArrayList<String> getStrings()
{
ArrayList<String> strings = new ArrayList<String>();
for (int i = 0; i < 24; i++)
{
strings.add("test: " + i);
}
return strings;
}
@Override
protected List<String> doInBackground() throws Exception
{
List<String> strings = getStrings();
for (int i = 0; i < strings.size(); i++)
{
publish(strings.get(i));
}
return strings;
}
@Override
protected void done()
{
try
{
List<String> strings = get();
for (String string : strings)
{
new StringDownloaderWorker(model, string).execute();
}
}
catch (Exception exp)
{
exp.printStackTrace();
}
}
}
public class StringDownloaderWorker extends SwingWorker<String, String>
{
private String currentString;
private UpdatableTableModel model;
public StringDownloaderWorker(UpdatableTableModel model, String string)
{
this.currentString = string;
this.model = model;
addPropertyChangeListener(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getPropertyName().equals("progress"))
{
StringDownloaderWorker.this.model.updateStatus(currentString, (int) evt.getNewValue());
}
}
});
}
@Override
protected String doInBackground() throws Exception
{
for (int i = 0; i <= 100; i++)
{
setProgress(i);
try
{
Thread.sleep(1000);
}
catch (InterruptedException ie)
{
//
}
}
return currentString;
}
}
}