I am creating my first JTable that requires me to create a custom AbstractTableModel
, TableCellEditor
, and DefaultTableCellRenderer
. Given that I have not needed to create these before, I've made some significant progress in getting my table to behave as desired.
However, I am getting overwhelmed with all the different methods I am overriding, and am spinning my wheels trying to figure out how to modify the ImageIcon of a particular cell. The cell must contain a JLabel, as it needs both an ImageIcon
as well as a text string. I can already set the initial ImageIcon
(although I am probably doing it incorrectly), but I can't set an updated ImageIcon
. Nothing fails, but no change is made.
In a general sense, what is the best way to get and set an icon to a JLabel
cell of a JTable
, assuming all of these models, editors, and renderers have already been instantiated?
My model has already been defined to return JLabel.class
for these cells, if you're wondering, and I also do a fireTableCellUpdated(row, col)
once the change has supposedly been made. If I do a System.out.println(getIcon())
before and after the update, I can even see the source has changed.
Here is some of the code (updated with URL/ImageIcon fix in place):
class MonitorTable extends JTable {
MonitorTableModel model = new MonitorTableModel(rows, columnNames);
setModel(model);
...
public void setIconAt(ImageIcon icon, int row, int col) {
model.setIconAt(icon, row, col);
} // End setIconAt(ImageIcon, int, int)
...
class MonitorTableModel extends AbstractTableModel {
...
public void setIconAt(ImageIcon icon, int row, int col) {
StatusTableCellRenderer cell =
(StatusTableCellRenderer)getColumnModel().getColumn(col).getCellRenderer().
getTableCellRendererComponent(myTableObject, null, false, false, row, col);
System.out.println(cell.getIcon()); // Shows initial icon source
cell.setIcon(icon);
fireTableCellUpdated(row, col); // Should update the table
System.out.println(cell.getIcon()); // Shows new icon source
System.out.println("Cell updated");
} // End setIconAt(ImageIcon, int, int)
} // End class MonitorTableModel
public class StatusTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int col) {
setIcon(imgGray);
setText((String)value);
return this;
} // End getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
} // End class StatusTableCellRenderer
} // End class MonitorTable
I fixed this by changing setIcon(imgGray)
to if (getIcon() == null) setIcon(imgGray);
.
The issue is my getTableCellRendererComponent
method was setting the icon to imgGray every time. Apparently my setIconAt
method, which calls getTableCellRendererComponent
, was being overridden, even though the "new" icon value was processed after the "old" value was (re)set.
I ended up removing all my setIcon
methods and moved the relevant logic into my StatusTableCellRenderer
class. That way I pass the value of the cell and let the renderer do the icon setting based on that value. It makes more sense this way, and works beautifully. I have confirmed that initial setting and all subsequent updates are performing as expected.
The logic of setting the icon is pretty simple -- set the predefined icon based on certain predefined threshold values.
double val;
if (getIcon() == null) setIcon(imgGray); // Initialize
if ((value == null) || (value == "")) {
val = 0;
} else {
val = Double.parseDouble(value.toString());
} // End if
if (val <= THRESHOLD1) {
setIcon(icon1);
} else if (val <= THRESHOLD2) {
setIcon(icon2);
...
} // End if
setText(value.toString());
I was very concerned about suggestions to make brand new objects to use, when the default JLabel
was exactly what I needed. It was both unnecessary and a potential performance hit to the JTable
. Thank you all for your insight and assistance. This was driving me batty!