Search code examples
javaswingcolorsjtablecellrenderer

Coloring a single cell in a JTable when representing Color objects


I have a JTable that is used to display numerous different data types in different rows. There are only two columns, where the left column is the variable name and the right column is the variable associated with the variable (so using a ColumnModel is not appropriate for my solution). So far this works really well for primitives, and the DefaultRenderer's for JComboBox's and JCheckBox's works well.

I am now trying to integrate java.awt.Color into the table, but I would like it rendered so that the cell is actually filled with the Color, and simply have no text (I don't believe that will be difficult at all when I get to that point). So far I have the functionality of the cell complete; clicking on the cell brings up a JColorChooser and returns the Color, where it will then be applied to the object that is associated with the cell. The code I am using for that is:

JTable table = new JTable(new CustomModel(columnNames, values))
{
    //Other functions inside here are excluded to keep it concise

    public void changeSelection(int row, int col, boolean toggle, boolean extend)
    {
        /*Row is a custom object I am using for each row in the table. Its purpose is to
        hold more information about the rows than I would normally be able to. Suffice
        to say for this example, it will be returning something with a Color in it*/
        Row obj = ((CustomModel)(table.getModel())).getRow(row);

        /*ObjectProperties is essentially a modified version of a hashmap that also
        stores the objects type among other things*/
        if(obj.getType() == ObjectProperties.TYPE.COLOR)
        {
            Color newColor = JColorChooser.showDialog(null, "Pick color", Color.RED);

            if(newColor != null)
            {
                table.getModel().setValueAt(newColor, row, col);
            }
        }
        super.changeSelection(row, col, toggle, extend);
    }
}

So this seems to work nicely, but now for the rendering. I figured if I tried to set a DefaultRenderer, it would work, so I used the line:

table.setDefaultRenderer(Color.class, new ColorRenderer());

My ColorRenderer class is as follows:

public class ColorRenderer extends JLabel implements TableCellRenderer
{
    public ColorRenderer() 
    {
        setOpaque(true);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col)
    {
        Color newColor = (Color)value;
        setBackground(newColor);

        return this;
    }
}

When I did use this however, I had no luck in displaying the actual Color, instead I got a String representation of the Color (I figure this means that my renderer isn't working at all). So I'm still yet to actually make it render the correct way, let alone have the ability to change the Color displayed when the user chooses something new.

I have been struggling with getting CustomRenderer's working for a long time now, having looked through every resource I could get my hands on. The tutorial at Oracle's JTable section was fairly useful I thought, but in the end did not work as I'd hoped. Other questions around this forum have been close to applying to my situation, but have often lacked the crucial element of applying to a particular object type, not just a particular cell. If I were to have numerous Color objects stored in my table, I would ideally like them all to behave the same way.

If there is something glaringly wrong or missing from my implementation, it would be fantastic to have this pointed out. Looking at the code closer (I have reread this before posting it numerous times to make sure I have included everything), I believe the issue may be in my Row class. It contains a method called getValue() that will return the object, so technically I could call something like:

if(rowObject.getValue() instanceof Color)
   //Apply renderer

But how could I do this with the code that sets a Default Renderer for a particular class?


Solution

  • No idea if its the best way to do this but overriding getCellRenderer(int row, int col) may help you:

    public TableCellRenderer getCellRenderer(int row, int col) {
        // Only care about the first column
        if (col == 1) {
            Row obj = ((CustomModel)(table.getModel())).getRow(row);
    
            // Check to see if this is a color
            if (obj.getType() == ObjectProperties.TYPE.COLOUR) {
                return super.getDefaultRenderer(Color.class);
            }
        }
    
        // Either this wasn't a color or it wasn't the first column, either way its super.getCellRenderer's problem now
        return super.getCellRenderer(row, col);
    }
    

    getCellRenderer(int row, int col) appears to only work with cell renderers places on columns so not much good if you want to only render a single cell differently.

    Another alternative that I have seen done is to define a cell renderer, apply it to the column and within your cell renderer's getTableCellRendererComponent(...) function determine which data type this is and whether or not to render it or to pass it onto another renderer.

    Personally I prefer the first option but if you had heaps of different custom cell types to render and wanted to keep all your rendering code in one class then I can see the appeal of the second option.