Search code examples
javaswinglook-and-feeltablecellrendereruimanager

How to create a Look&Feel aware TableCellRenderer?


All the examples I can find of custom TableCellRenderers all seem extend DefaultTableCellRenderer. Nothing wrong with that, I guess, except that you do not get to extend the TableCellRenderer functionality of whatever is the current Look&Feel. This is bad. Your TableCellRenderer is then not L&F aware.

That is in fact exactly what I want to do: I want to create a custom TableCellRenderer that is L&F aware, so rather than extending DefaultTableCellRenderer it should extend (or decorate) whatever is the TableCellRenderer which the L&F has installed by default for the given class type.

I can get to that by doing myJTable.getDefaultRenderer(...) but that will not give me a new instance of that class, it will simply return the L&F's 'master' table cell renderer to be used for that type of class. I started out with a solution that created a custom TableCellRenderer that wrapped the instance that I got from the mentioned method (effectively using a Decorator pattern) until I realized that I didn't have my own private instance of a renderer. If I mess with that instance of renderer I cannot do column-specific rendering as any change I do on that instance will affect more columns than intended.

So I've concluded that what I need to do is to create a completely new instance of the L&F's TableCellRenderer for that class-type. (in my case class type = Object). I believe I can get to the relevant class name from somewhere in UIManager, but I wouldn't know which key to use. Secondly then - unfortunately - I believe I would have to use Reflection to actually instantiate an object of that TableCellRenderer class.

Are my assumptions correct, i.e. there's unfortunately no factory for TableCellRenderers? and how would I actually instantiate a new instance of my current L&F's TableCellRenderer for class-type Object ? (I know how to use Reflection, if that's the only way)

UPDATE

As you may have guessed I'm testing with a proprietary L&F as well as the standard ones. This particulary proprietary L&F installs its own TableCellRenderers which is perfectly legal, IMO. The Java Synth L&F does the same. So I don't want to make this a question of a particular L&F.

Test: I create a table with two String columns. I install my own custom cell renderer on one of the columns, but not the other. My custom renderer looks like this:

public class CustomCellRenderer extends DefaultTableCellRenderer {
    @Override
    public Component getTableCellRendererComponent(
              JTable table, Object value, boolean isSelected, boolean hasFocus,
              int row, int column) {
        return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    }
}

As you can see the custom renderer does nothing. Since it does nothing I should get the same rendering effect on both table columns. I don't! The reason is of course that by simply extending DefaultTableCellRenderer I'll not 'inherit' the L&F's own TableCellRenderers should it have one/some. On many, many L&F's the above test will in fact render the two columns the same way, but that's more by chance. I want to do things the right way. The fact that extending DefaultTableCellRenderer produces expected results with most L&Fs is not enough for me. :-)


Solution

  • We solved this Problem in like this:

    public class CustomCellRenderer  implements TableCellRenderer {
        //Use appropriate class here
        private final TableCellRenderer defaultTableCellRenderer= new JTable().getDefaultRenderer(Object.class);
        @Override
        public Component getTableCellRendererComponent(
                  JTable table, Object value, boolean isSelected, boolean hasFocus,
                  int row, int column) {
            Component c = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            // do custom stuff to c here ...
            return c;
        }
    }
    

    If you don't create a new Instance for the TableCellRenderer you get funky effects, because you change the tables default instance.