我正在使用装饰器设计模式暗示TableCellRenderer
s。只要我所需要的只是以可以在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);
}
}
我想到了 2 种不同的解决方案:
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
对象执行,只有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;
}
并在 DefaultTableCellRenderer 的子类中使用 paint
方法,而不是像这样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);
}