Как украсить TableCellRenderer инструкциями, связанными с графикой?

1

Я использую TableCellRenderer используя шаблон дизайна Decorator. Все работает хорошо и хорошо, пока все, что мне нужно, - это украсить возвращаемый компонент из декорированного рендеринга таким образом, который может выполняться внутри области getTableCellRendererComponent(..).
Но как я могу украсить возвращаемый компонент для таких случаев, которым нужен объект Graphics в процессе рисования? В частности - внутри метода paintComponent(Graphics g)?
Например, когда я хочу нарисовать определенную строку, где простого setBorder(..) будет недостаточно:

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);
    }
}

У меня было два разных решения:
1. Ввести интерфейс под названием DecoratedTableCellRenderer:

import javax.swing.table.TableCellRenderer;

public interface DecoratedTableCellRenderer extends TableCellRenderer {

    public void setPostPaintComponentRunnable(Runnable postPaintComponentRunnable);
}

Итак, теперь MyTableCellRendererDecorator получит в своем конструкторе DecoratedTableCellRenderer вместо простого TableCellRenderer, а ответственность за декорирование внутри paintComponent переходит к украшенному классу. Если мы предположим, что рендерером является JComponent который красит себя, это можно сделать, переопределив paintComponent(..) и применив postPaintComponentRunnable.run() после его собственного кода рисования.
Но что, если я хочу поддерживать такие средства визуализации декора, которые будут применяться на любом TableCellRenderer в котором я не могу изменить?
2. Использование java-отражения и динамического прокси-сервера для создания экземпляра нового ComponentUI для decoratedComponent ComponentUI, который будет выполнять каждый метод в качестве исходного объекта ComponentUI только с украшенной версией paint(Graphics g, JComponent c).
Это будет нести ответственность за оформление в классе украшения, но динамические прокси-серверы всегда трудно читать и поддерживать в моей перспективе, я был бы очень рад, если бы нашел более элегантную идею.

  • 0
    DecRenderer показывает один подход; связанный пример виден здесь .
  • 0
    Спасибо peeskillet, создание TableCellRenderer, которое реализует Icon, действительно является хорошим направлением. В конце концов я использовал декоратор TableCellRenderer, который расширяет JComponent, добавляет декорированный компонент к себе и возвращает сам себя - таким образом он может управлять своим собственным paintComponent. Таким образом, я могу добавить графические операции и не переопределять значок оформленного компонента, если он уже есть.
Теги:
user-interface
swing
rendering
jtable

1 ответ

0

Как оказалось, исходный образец кода, который вы дали, близок к правильному, потому что на самом деле возвращаемым компонентом является this. То есть getTableCellRendererComponent возвращает вызываемый DefaultTableCellRenderer, поэтому в процессе рисования компонента будут вызываться любые переопределенные методы в вашем подклассе DefaultTableCellRenderer.

Вот код для DefaultTableCellRenderer, который показывает, о чем я говорю:

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

Поэтому, если вы переопределите метод paint(Graphics gfx) и вызовите суперкласс класса getTableCellRendererComponent вы сможете рисовать все, что хотите в ячейке.

Поэтому ваш getTableCellRendererComponent должен читать следующее:

@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;
}

И используйте метод paint в вашем подклассе DefaultTableCellRenderer вместо paintComponent следующим образом:

/**
 * 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);
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню