1

I'm implemting TableCellRenderers using the Decorator design-pattern. All works nice and well as long as all I need is to decorate the returned component from the decorated renderer in such manner that can be performed inside the scope of getTableCellRendererComponent(..).
But how can I decorate the returned component for such cases which need the Graphics object in the paint process? In particular - inside his paintComponent(Graphics g) method?
For example, when I want to draw a certain line where a simple setBorder(..) will not be suffice:

import java.awt.Color;
import java.awt.Component;

import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.DefaultTableCellRenderer;


public class MyTableCellRendererDecorator extends DefaultTableCellRenderer {

    private TableCellRenderer decoratedRenderer;
    private Component decoratedComponent;

    public MyTableCellRendererDecorator(TableCellRenderer decoratedRenderer) {
        super();
        this.decoratedRenderer = decoratedRenderer;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {

        this.decoratedComponent = decoratedRenderer.getTableCellRendererComponent(table, value,
            isSelected, hasFocus, row, column);

        //an example for a decoration which works fine
        decoratedComponent.setBackground(Color.red);

        return decoratedComponent;
    }

    /**
     * This code will NOT be executed, because the paint will be called on the returned component
     * from getTableCellRendererComponent() and NOT on this JLabel in which this renderer subclasses.
     */
    @Override
    public void paintComponent(Graphics g) {
        decoratedComponent.paint(g);
        //an example for a needed decoration in paintComponent()
        Rectangle bounds = g.getClipBounds();
        g.setColor(Color.BLUE);
        g.drawLine(0, 0, bounds.width, bounds.height);
    }
}

I had 2 different solutions in mind:
1. Introduce an interface called DecoratedTableCellRenderer:

import javax.swing.table.TableCellRenderer;

public interface DecoratedTableCellRenderer extends TableCellRenderer {

    public void setPostPaintComponentRunnable(Runnable postPaintComponentRunnable);
}

So now MyTableCellRendererDecorator will receive a DecoratedTableCellRenderer in his constructor instead of a simple TableCellRenderer, and the responsability for decorating inside paintComponent moves to the decorated class. If we assume a renderer is a JComponent which paints itself, this can be done by overriding paintComponent(..) and applying postPaintComponentRunnable.run() after his own paint code.
But, what if I want to support such decoration renderers which will apply on any TableCellRenderer in which I may not be able to modify?
2. Using java's reflection and a dynamic proxy to instantiate a new ComponentUI delegate to decoratedComponent, which will perform each method as its original ComponentUI object, only with a decorated version of paint(Graphics g, JComponent c).
This will keep the decoration responsibilty in the decorating class, but dynamic proxies are always somewhat hard to read and maintain in my perspective, I would be very happy if I will find a more elegant idea.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
oleiba
  • 550
  • 1
  • 4
  • 12
  • [`DecRenderer`](http://stackoverflow.com/a/2834484/230513) shows one approach; a related example is seen [here](http://stackoverflow.com/a/21756629/230513). – trashgod Apr 26 '14 at 01:50
  • Thanks peeskillet, creating a TableCellRenderer which implements Icon is indeed a good direction. Eventually I used a decorator TableCellRenderer which extends JComponent, and adds the decorated component to itself, and returns itself- thus it can control it's own paintComponent. That way I can add Graphics operations, and don't override the decorated component's icon, if already has one. – oleiba May 13 '14 at 13:38

1 Answers1

0

As it turns out, the original sample code you gave is close to correct because, in fact, the returned component is this. That is, getTableCellRendererComponent returns the DefaultTableCellRenderer it was called on, so any overridden methods in your subclass of DefaultTableCellRenderer will be called during the painting of the component.

Here's the code for DefaultTableCellRenderer that shows what I'm talking about:

public Component getTableCellRendererComponent(JTable table, Object value,
                      boolean isSelected, boolean hasFocus, int row, int column) {
    //...
    return this;
}

Therefore, if you override the paint(Graphics gfx) method and call your super classes getTableCellRendererComponent you will be able to draw whatever you want in the cell.

So your getTableCellRendererComponent should read like this:

@Override
public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {

    JLabel component = super.getTableCellRendererComponent(table, value,
        isSelected, hasFocus, row, column);

    //an example for a decoration which works fine
    component.setBackground(Color.red);

    return component;
}

And use the paint method in your subclass of DefaultTableCellRenderer instead of paintComponent like this:

/**
 * This code WILL be executed.
 */
@Override
public void paint(Graphics g) {
    super.paint(g);
    //an example for a needed decoration in paintComponent()
    Rectangle bounds = g.getClipBounds();
    g.setColor(Color.BLUE);
    g.drawLine(0, 0, bounds.width, bounds.height);
}
Jason
  • 11,709
  • 9
  • 66
  • 82