Working around BasicTableUI’s lack of a prepareRenderer hook

June 27, 2009

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:

iTunesTable-bad
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!

iTunesTable-good

4 Responses to “Working around BasicTableUI’s lack of a prepareRenderer hook”


  1. […] another post, Ken Orr posts how to work around BasicTableUI’s lack of a prepareRenderer hook. This is relevant if you want to customise how a JTable looks in […]

  2. Alex Says:

    Hi, I saw in your screenshot a “rating” swing component a custom cell renderer, I’ve been looking for a JComponent like that, how do u found it?, I’ve been trying search with terms like “rating swing component” but no luck. Any advice will help, thnx. BTW great work with MacWidgets

    • Ken Says:

      Hi Alex,

      I’m not exactly sure I understand your question, but if you’re looking for the rating cell renderer code, you’ll find that here.

      -Ken

      • Alex Says:

        Hi Ken, hehehe I think I should first take a look at your MacWidgets Library, there’s the RatingComponent you mentioned in sexy-swing-app-the-rating-renderer, Thnx in advance for your help.


Leave a comment