Stevo
Stevo

Reputation: 417

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?

Upvotes: 1

Views: 478

Answers (2)

Darc
Darc

Reputation: 863

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.

Upvotes: 1

Martin Frank
Martin Frank

Reputation: 3454

you must override the methode getColumnClass(int c) int your tablemodel (best you use DefaultTableModel...)

class MyTableModel extends AbstractTableModel {
    private String[] columnNames = ...//same as before...
    private Object[][] data = ...//same as before...

   //taken from http://docs.oracle.com/javase/tutorial/uiswing/components/table.html

    public Object getValueAt(int row, int col) {
        return data[row][col];
    }

    public Class getColumnClass(int c) {
        //here - check that the proper class is returned!
        return getValueAt(0, c).getClass();
    }

}

Upvotes: 0

Related Questions