通常在使用
JTable
或 JTree
时,用户定义自己的单元格渲染器。
从
DefaultTableCellRenderer
继承用户组件,并实现渲染器方法getTableCellRendererComponent
是很常见的。事实证明,DefaultTableCellRenderer
实际上继承自JLabel,因此在调用super(在渲染方法中)时返回自身(this),因此用户的渲染器也可以类似地返回自身(this)。
一切都很顺利。
我的问题是怎么会这样?
表每次调用此方法时,都会给出不同的参数,并且输出标签会根据这些参数的函数进行更改。如果它确实是标签的同一个实例 - 难道它不应该根据上次调用此方法进行更改吗? 这是否意味着表格的所有单元格都由相同的标签实例组成,该实例具有相同的值(上次调用渲染器方法的值)?
我搜索了网络,并深入研究了 Swing 的代码,但找不到任何 clone 或 copy constructor 的行为实际上复制了输出标签。 我找不到任何证据表明(也许)swing 使用反射来每次从头开始重新实例化渲染器。
我已经阅读了 Swing 的 JTables 教程,在那里我可以找到下几行:
您可能希望表格中的每个单元格都是一个组件。然而,出于性能原因,Swing 表的实现方式有所不同。 相反,单个单元格渲染器通常用于绘制包含相同类型数据的所有单元格。您可以将渲染器视为可配置的印章,表格使用它来将适当格式的数据印记到每个单元格上。当用户开始编辑单元格的数据时,单元格编辑器接管该单元格,控制单元格的编辑行为。
他们给出了一个暗示,我所说的确实是正确的,但没有解释它实际上是如何实现的。
我听不懂。你们谁都可以吗?
它是享元模式的实现。
当 JTable 重新绘制自身时,它会启动一个循环并迭代必须绘制的每个单元格。
对于每个单元格,它使用与该单元格相对应的参数调用渲染器。渲染器返回一个组件。该组件绘制在与当前表格单元格相对应的矩形中。
然后为下一个单元格调用渲染器,并将返回的组件(例如,具有不同的文本和颜色)绘制在与单元格对应的矩形中,等等。
想象一下,每次调用渲染器时,都会获取返回组件的屏幕截图并将其粘贴到表格单元格中。
除了@JB对JTable
和
JTree
如何使用flyweight模式的明确解释之外,请注意这两个类如何提供公共方法getCellRenderer()
和getCellEditor()
。检查这些方法以了解JTable
如何使用类文字作为运行时类型标记来按类选择渲染器或编辑器(如果列未指定任何渲染器或编辑器)。在内部,JTable
使用 Hashtable defaultRenderersByColumnClass
作为实例存储。
经过一番挖掘,从 DefaultTableCellRenderer 文档中找到了下一个实现说明:
实现说明:该类继承自标准组件类JLabel。然而,JTable 采用独特的机制来渲染其单元格,因此需要对其单元格渲染器进行一些稍微修改的行为。 table 类定义了一个单元格渲染器,并将其用作渲染表中所有单元格的橡皮图章;它渲染第一个单元格,更改该单元格渲染器的内容,将原点移动到新位置,重新绘制它,等等。标准 JLabel 组件并非设计用于这种方式,我们希望避免每次绘制单元格时触发重新验证。这会大大降低性能,因为重新验证消息将沿着容器的层次结构向上传递,以确定是否有任何其他组件受到影响。由于渲染器仅在绘画操作的生命周期内成为父级,因此我们同样希望避免与遍历绘画操作的层次结构相关的开销。因此,此类将 validate、invalidate、revalidate、repaint 和 firePropertyChange 方法重写为无操作,并重写 isOpaque 方法只是为了提高性能。如果您编写自己的渲染器,请记住这一性能考虑因素。
这基本上就是 JB 上面所解释的。
感谢您的(快速)回答