对于未知技术的原理,我总喜欢自己想像它是如何实现的。我上学时互联网搜索技术还没有怎么流行。大家学习新技术的方法,总是第一去查教科书,第二去找论文,第三去问师兄。而我想知道的技术往往都是教科书上找不到,论文中很少提及的,师兄们很少去想的事情。于是很多时候就自己想像它们是如何实现的。
图形界面设计技术对我来说就是这样。我很早以前就对这种技术很感兴趣,但是能够查找到的资源很少。我常常自己冥想图形设计界面是如何画出来的;描述界面的元数据和代码是如何解析和映射的;界面组件属性是如何设置和查看的。后来随着对于Swing原理和技术的深入了解,使得我对实现界面设计工具越来越胸有成主,终于在2002年某个时候,大胆动手,一气呵成。后来又分别在2003年和2005年分别做过一个,对于其中具体实现技术又有了更深刻的理解。
言归正传,前文提到的三个问题也是Swing界面设计工具首先要解决三个问题。
Swing图形设计工具界面的绘制一般使用Swing的Renderer思想实现。Renderer思想在前面许多文章中都有提及,其基本原理是将渲染组件的图形对象传递给其他组件诸如paint、paintAll、paintChildren、paintComponent等渲染方法,让该组件代理完成具体界面的绘制。这种技术经常在JTable、JList、JTree和JComboBox等复杂Swing组件中使用。Renderer思想是代码复用和动态绑定思想在Swing架构中的具体体现。它包含的一个深层含义是,对于具体组件外观的绘制,负责渲染的宿主组件不需要知道被绘制组件的绘制是如何进行的,只要将自己的图形设备对象传递给被绘制组件的渲染方法就可以了。当然现实中的图形界面设计工具还需要渲染其他辅助图形,如当前选定组件的边框等。而这些都不会是什么问题了。
因此Swing界面设计工具一般首先要在内存中构建被设计界面的组件树,当要绘制设计工具的界面时,只需将其Graphics对象传递给组件树顶层容器的paint方法就可以了。
如何构建被设计界面组件树呢?这个界面组件树的数据结构由描述界面的元数据,甚至是界面源代码码解析生成。界面元数据或源代码的解析生成是界面设计工具中最关键的技术之一。目前主要有三种解决方法。第一种是Delphi、VB为代表的资源文件方式,第二种是NetBeans Mattise为代表的xml界面描述文件方式,第三种是Eclipse VE为代表的代码解析和生成技术。Java界面设计工具一般采用后两种方式。
界面描述文件方法相对比较简单。其核心思想是将界面组件树采用xml文件(或其它格式)的方式保存起来,以后或者预先生成静态界面源代码代码,或者在运行时根据它动态生成界面。由于xml的解析要比java代码的解析简单得多,所以采用这种方式实现比较简单,而且界面设计动作容易控制。缺点是必须维持描述文件和源代码文件的同步,另外这种xml配置文件语法结构往往是非公开的,使得它设计的界面文件不能被其他界面设计工具所复用。另外程序员不能随意修改生成代码,描述文件和源代码之间生成关系是单方向的。
第三种直接将Java界面源代码解析成组件数。这种方法也有两种做法,一种是对代码进行语法语义分析,并构建出要设计的界面组件树来;一种是使用Java编译器将源文件编译成最终class,使用类加载器动态加载并反射生成最终组件树。前者缺点是非常复杂,需要丰富编译知识;优点是不需要将源代码编译通过。后者优点是解析非常简单,全部交给了编译器完成;缺点是如果源码存在编译错误,就无法最终生成设计界面。
第三个基本技术,即组件属性的反射一般使用Java Beans技术实现。Java平台提供了解决界面设计工具组件反射的工具,如BeanIntrospector、BeanDescriptor等类,它们建立在Java反射技术的基础上,提供解析和设置JavaBean(主要是组件对象)属性的机制。用来查看和设置组件属性的控件通常称作属性页,在Swing可以通过表格JTable结合Renderer/Editor思想实现。
今天的文章先就Swing界面设计工具的基本思想和技术做一个简单的概述,后面将就这三个方面的技术在图形界面设计工具的应用进行详细地描述。