Swing组件根据其所操作的数据类型分为两种。标量数据类型的组件操作的是基本类型的数据,如字符串、布尔、数字等,此类型组件包括JTextField、JCheckBox、JLabel、JButton等。复合数据类型的组件操作的是诸如向量、矩形和非线形等类型的数据。向量数据类型的组件有JComboBox、JList,矩形数据类型的组件有JTable,非线形数据类型的组件如 JTree。
为更形象地展现各种类型的数据,复合数据类型的组件往往采用标量数据类型组件来表现每种数据元素。比如JTable的某一列数据是字符串类型,那么该列的单元格往往用JLabel方式展现每个字符串;如果一列数据是布尔类型,那么该列的单元格往往用JCheckBox方式展现每个布尔值。
如何实现复合数据类型的组件的渲染呢?最直接的是在paint方法中一个一个地根据数据类型画出每一个组件,但这种方法很显然代码复用率很低,大量重复了相应标量型组件的代码,代码的维护和同步会非常困难,也不容易实现皮肤切换。
为解决此问题,Swing体系中提出了所谓渲染器Renderer的概念,其核心思想是使用接口,封装和复用已有标量型组件的渲染代码,降低代码重复率,提高组件的可扩展性。
如何使用渲染器返回的组件渲染当前的单元格呢?JTable在自己内部隐藏了一个所谓的CellRendererPane组件,该组件是一个“零实现”的容器组件。虽然被添加到JTable上,但它是不可见的,其paint和update方法都为空,仅仅作为临时容纳渲染组件的容器,目的是将渲染组件粘合到JTable组件树上,使得渲染组件有效化,以便使它们达到渲染前的正确状态。下面代码演示了CellRendererPane的概要结构:
- public class CellRendererPane extends Container implements Accessible
- {
- //构造函数
- public CellRendererPane() {
- super();
- //注意CellRendererPane的布局管理器为空,后面渲染时有用!
- setLayout(null);
- //不可见,使之不被显示在JTable上
- setVisible(false);
- }
- //零实现
- public void invalidate() { }
- //零实现
- public void paint(Graphics g) { }
- //零实现
- public void update(Graphics g) { }
- ......
- //下面是CellRendererPane的paintComponent方法:
- public void paintComponent(Graphics g, Component c, Container p,
int x, int y, int w, int h, boolean shouldValidate) {- ......
- if (c.getParent() != this) {
- //如果渲染组件c还没有添加当前CellRendererPane中
- //添加进去
- this.add(c);
- }
- c.setBounds(x, y, w, h);
- if(shouldValidate) {
- c.validate();
- }
- //下面主要处理双缓冲问题,可略去
- ......
- //准备图形对象
- Graphics cg = g.create(x, y, w, h);
- try {
- c.paint(cg);
- }
- }
- }
渲染器Renderer的核心思想都体现在上面红色代码标注的部分。将JTable的图形对象传递给组件的paint的方法,产生的结果是将组件画到了JTable上。其实Swing打印的原理也大抵如此,只不过这儿的图形对象变成了打印机的图形对象。虽然大部分Swing组件都专门对打印进行了专门的处理(主要是因为有一些图形元素不希望被打印的,比如填充的内容往往不希望打印,可能是太耗墨了),但基本过程是一样的。
渲染器的思想很像是摄像机、镜子等成像原理。作个比喻,如果你想获取某人的图像,一种方法是将此人一点点用笔画出来。另种方法是通过光线将此人照到镜子里或用照相机拍摄下来。其好处是不管是什么物体,都可以映射出来,具有很强的可扩展性。比如JTable中,表格中不仅可以使用JLabel、 JCheckBox、JComboBox等简单组件作为渲染器,而且可以使用其它任何的Swing组件进行渲染,包括复杂的组件JTable(比如实现表格套表的风格)、自定义的组件,渲染器方法带来的好处不仅仅是组件代码的复用,更带来了无限的可扩展性!
渲染器思想在Swing中有着广泛的应用。除利用它们实现JTable、JList、JTree和JComboBox等标准组件,还可以实现界面设计工具中属性页、类似UML设计图、类似于MS Excel风格的电子表格等更为复杂的界面组件,甚至IDE中常见的界面设计工具也是利用了渲染器的思想,它把整个组件树当作一个大渲染器,渲染出当前图形用户界面的设计效果。
渲染器是Swing展现复杂数据结构的利器。但是Swing组件不仅仅被用作展现数据,通常还是编辑数据的地方。实际上纯粹展现数据的Swing组件很少,标准组件中也许只有JLabel。复合数据类型的组件往往使用渲染器原理实现组件的渲染,使用所谓in-place editor实现组件的编辑。渲染器Renderer和Editor的结合赋予了Swing强大的灵活性,JTable、等组件这两种原理结合的代表。后续文章将讲述in-place editor在Swing中的使用。
【编辑推荐】