Я использую 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)
.
Это будет нести ответственность за оформление в классе украшения, но динамические прокси-серверы всегда трудно читать и поддерживать в моей перспективе, я был бы очень рад, если бы нашел более элегантную идею.
Как оказалось, исходный образец кода, который вы дали, близок к правильному, потому что на самом деле возвращаемым компонентом является 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);
}
DecRenderer
показывает один подход; связанный пример виден здесь .