我正在使用装饰器设计模式实现TableCellRenderer
。
只要我需要以可以在 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.引入一个名为DecolatedTableCellRenderer
的接口(interface):
import javax.swing.table.TableCellRenderer;
public interface DecoratedTableCellRenderer extends TableCellRenderer {
public void setPostPaintComponentRunnable(Runnable postPaintComponentRunnable);
}
现在,MyTableCellRendererDecorator
将在其构造函数中收到一个 DecolatedTableCellRenderer
,而不是一个简单的 TableCellRenderer
,并且负责在 paintComponent 内部进行装饰
移动到装饰类。如果我们假设渲染器是一个可以自行绘制的 JComponent
,则可以通过重写 paintComponent(..)
并应用 postPaintComponentRunnable.run()
来完成> 在他自己的绘制代码之后。
但是,如果我想支持此类装饰渲染器,并将其应用于我可能无法修改的任何 TableCellRenderer
上,该怎么办?
2. 使用java的反射和动态代理实例化一个新的ComponentUI
委托(delegate)给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)
方法并调用您的父类(super class) 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);
}
关于java - 如何用图形相关指令装饰TableCellRenderer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23305319/