Table Row Rendering
Posted by Rob Camick on January 24, 2010
There are times when you might want to do some custom rendering for an entire row of a table. Based on answers I see in the forums, for most people the first thought is to create a custom renderer. This can be a simple solution in some cases, but are there other alternatives to consider as well?
A renderer is used by a JTable to render a class of data for a given column. So when all the data in the table is of the same class, String for example, then the easiest solution is probably to create a custom String renderer containing your custom row rendering logic.
However, what happens when your table contains multiple classes of data, maybe String, Date, Integer and Boolean? Would you create 4 custom renderers or is there a better way?
In this situation it is possible to write the custom rendering code only once and have it applied to all renderers. The trick is to override the prepareRenderer(…) method of JTable. This method is invoked every time a cell is rendered so it is the perfect place to add row level rendering logic that can be applied to all renderers.
A simple example for overriding this method would be as follows:
JTable table = new JTable(...) { public Component prepareRenderer( TableCellRenderer renderer, int row, int column) { Component c = super.prepareRenderer(renderer, row, column); // Alternate row color if (!isRowSelected(row)) c.setBackground(row % 2 == 0 ? getBackground() : Color.LIGHT_GRAY); return c; } };
You can use this technique to alternate the background color of each row:
… add a row level Border:
… change the background color based on data in the row:
Although this tip was suggested to be used for row rendering. It can be considered any time you want to add common logic to multiple different renderers.
tbee said
I’m using this beauty of a hook-in method for all kinds of goodies, like for example:
– highlight a cell when the value has an error or warning (by wrapping it in a JPanel with a red / orange border)
– automatically adjust the height of a row if the renderer requires additional room
This saves the trouble of implementing logic in each and every cell render implementation.
Rob Camick said
Some interesting ideas. There are many others. I have some old code lying around that also does cell blinking. This is one of my favourite methods to override.
Kleopatra said
Rob,
exactly what SwingX does – formalized into “Highlighter” which are pluggable by table (and list, tree) or by column :-)
The one problem with this approach is that the renderer must play strictly by the rules, that is reset all visual properties in each call to getXXCellRenderingComponent. Most default renderers are misbehaving in one way or another, particularly unfortunate is DefaultTableCellRenderer which doesn’t do so with its colors.
@Tom, careful: size changing in the prepare can lead to changing the state of the table during the a paint cycle which is a Bad-Thing
Cheers
Jeanette
Rob Camick said
As usual, good point about resetting the renderer state. At least my examples take that into account, at least I have not noticed any problems.
Once I get back from vacation I will update the posting to specifically mention this potential problem.
Denis Lemberger said
Setting the background color using this technique doesn’t work when the renderer is a subclasses of DefaultTreeCellRenderer. It’s background simply remains white.
Actually setting the background color of a DefaultTreeCellRenderer doesn’t work at all (not even when overriding getTreeCellRendererComponent())!
Any hints?
Many thanx in advance
:-Denis
Rob Camick said
Sorry, I”m not familiar with the details of the Tree renderer. You can try looking at the source code for the default Tree and Table renderers to see why they work differently.
tbee said
That is exactly why I used the JPanel for my error / warning border; nicely under my own control. Even tried doing the alternating background using that, but the renderer has to be transparent then, and that usually is not the case.
@Jeanette: no problems so far, but as soon as I run into one I’ll “invokeLater” the call
I just saw that I use the same hook to make the component look disabled if the cell is not editable.
SongLi said
Thanks for this solution. However, when I added row sorter, the table color will not change after row sorting. Any suggestion about this problem?
Rob Camick said
When using a table you need to understand the difference between working with the “model” or the “view” when accessing the data. The example code for the “Data” tab has been updated to include sorting on the table and all indexes are converted to model values in the prepare renderer code.
Bob said
Sorry about this silly question, but following your example, my prepareRederer isn’t being called, so my rows aren’t rendered. Could you help me? What am I doing wrong?
Tbee said
Try adding a @Override to the method, see if you are really overriding.
sam said
Thanks so much for this tutorial. I’m a C++ coder moving to Java and this really helped me a lot.
Have an eBeer on me!
Sam
a said
Thank you so much dude! I kept having the problem that my jtable only set the back ground of two rows using a custom default renderer – both of them strings and couldn’t figure out what that was! In addition if I select a row then the background and font color is also white – so I can’t see the text!!!!! This has solved both problems!!!
John Paul Jai said
I made of this today :) And found the difference between custom renderer and prepareRenderer() :) Thanks again Rob Camick…
Rob Camick said
Glad it helped.
rishu said
If I have a row sorter enable for each column , then this does not help. I mean if u sort rows by double clicking on a column header, the color changes are sticky to cells and do not get reflected on the correct data.
Rob Camick said
If you are getting data from the model then you will need to use:
and then access the model data using the modelRow variable.
Anonymous said
Hi,
I’ve used your tip for painting odd rows, but it happens that the selection color is not shown in those rows. Any idea why would it be?
Thanks
Robert Schmidt said
Hi Rob!
After two days searching the web, I found your code. You saved my day! Everything else I tried didn’t work using NetBeans, this does.
Great work.
Thanks
Chakib said
Thanks a lot ;)
it’s a good job