If you’ve ever extended BasicTableUI, you may have struggled a bit with it’s inability to hook into the cell rendering process. JTable has the prepareRenderer method through which you can inject yourself into the cell painting pipeline. BasicTableUI, however, has no such mechanism.
Why would we even need this capability from a BasicTableUI? In ITunesTableUI, for example, I install a special border on the containing JScrollPane, which paints the row striping (the subject of a future blog entry). In order for the striping to show through, each renderer must be non-opaque (transparent). By default, renderers are opaque, which results in a table that looks like this:
![]()
We have a couple of options to work around this. First, we could simply grab each of the default renderers and make it non-opaque. This isn’t a great solution, though, because there’s no easy way to get a list of all these renderers — we’d end up harding coding a set into our UI delegate. And if downstream consumers added their own renderers, they’d be responsible for making sure they were non-opaque.
The second (and better) option, is to create a custom CellRendererPane that essentially gives us a prepareRenderer method. CellRendererPane is the class used to stamp out each table cell onto the screen. The renderer for the cell is prepared, and then added to the CellRendererPane, which manually invokes paintComponent. Overriding paintComponent gives us the hook into the cell painting process that we’re looking for. Here’s what the custom CellRendererPane looks like:
/**
* Creates a custom {@link CellRendererPane} that sets the renderer component to
* be non-opaque if the associated row isn't selected. This custom
* {@code CellRendererPane} is needed because a table UI delegate has no prepare
* renderer like {@link JTable} has.
*/
private CellRendererPane createCustomCellRendererPane() {
return new CellRendererPane() {
@Override
public void paintComponent(Graphics graphics, Component component,
Container container, int x, int y, int w, int h,
boolean shouldValidate) {
// figure out what row we're rendering a cell for.
int rowAtPoint = table.rowAtPoint(new Point(x, y));
boolean isSelected = table.isRowSelected(rowAtPoint);
// if the component to render is a JComponent, add our tweaks.
if (component instanceof JComponent) {
JComponent jcomponent = (JComponent) component;
jcomponent.setOpaque(isSelected);
}
super.paintComponent(graphics, component, container, x, y, w, h,
shouldValidate);
}
};
}
Here’s how we create and install this custom CellRendererPane in our extension of BasicTableUI:
CustomTableUI extends BasicTableUI {
// ...
@Override
public void installUI(JComponent c) {
super.installUI(c);
table.remove(rendererPane);
rendererPane = createCustomCellRendererPane();
table.add(rendererPane);
// ...
}
}
and that lets us make all our non-selected cells non-opaque!
iPhone to the rescue
December 22, 2008
![]()
I was one of the poor saps in southern New Hampshire who lost his power in the recent ice storm – me and 430,000 NH residents! Not only did I lose my power, but it wasn’t restored for a week – that’s a painfully long time to be disconnected.
Fortunately for me, I could stay connected with my relatively new and shiny iPhone. I think I would have gone stir crazy without it. This unusual event made the $70 monthly fee seem oh-so worth it!
Yet another blog
April 22, 2008
At the risk of being left completely in the 20th century, I’ve finally broken down and started a blog. Mainly because I want a place to post sample code and screen shots from my upcoming Java One presentation (Simply Sweet Apps with Glazed Lists).
Other than for my immediate content sharing needs, you’ll probably find sexy UI stuff here, mostly in Java – so if your into that kinda thing, stay tuned.