GUI 应用程序是软件的一种主要类型,所以Java的GUI库应该是标准化并被集成到JRE平台中的。然而不同的操作系统有不同的GUi风格和组件集。有一些组件在所以平台上有相似的观感。这些共有组件如按钮,标签,文本域,单选框等被称为标准组件。不同的GUI工具集提供了不同的组件集。GUI工具集总是遵循不同的原则来选择组件类型和特征以实现。考察一个工具集,有两个不同的要素:组件类型和组件特征。
Terms
首先让我图解两个数学概念:最大公约数和最小公倍数。三个集合代表不同的操作系统。相交的部分是最大公约数,合并的部分是最小公倍数。
现在让我们来考察Java GUI工具集AWT,SWT和Swing的区别
AWT
AWT 组件集遵循最大公约数原则,即AWT只拥有所有平台上都存在的组件的公有集合。所以你在AWT中无法获取如表或树等高级组件,因为它们在某些平台上不支持。AWT的组件特征同样遵循这一原则。它只提高平台上公有的特征。例如AWT按钮不能附着图片,因为在Motif平台上,按钮是不支持图片的。
由于它低劣的组件集和特征,AWT无法吸引开发者。它是Sun不推荐使用的,只是为了确保向下兼容和支持Swing。
SWT
SWT 最初的目标之一是为了提供比AWT更为丰富的组件集。它遵循最小公倍数原则以提供一个各个平台上包含的组件的并集。思路是如果一个组件在某个平台上包含,那么SWT就会包装它并用java代码和JNI来调用它。如果一个组件在某一平台上不存在,它就会用继承并绘制Composite的方式来模拟组件。一个 SWT Composite类似于AWT的Canvas。以这种方式,SWT提供了较AWT更为丰富的组件集。值得指出的是SWT的JNI封装不同于AWT,它的模拟也不同于Swing。
在组件特征方面,SWT类似于AWT。它遵循最小公倍数原则。在早期的SWT版本中,SWT按钮因为和AWT同样的原因不支持附着图片。在之后的版本中,许多缺失的特征采用模拟的方式补全。但仍有许多特征无法采用纯粹的模拟实现。SWT将组件的控制交给本地操作系统。它难以扩展。只有例如图形装饰等特征可以借助模拟绘制来自定义实现。所以严格意义上将,SWT组件的组件集和特征因其难于扩展而不如Swing来得丰富。
Swing
Swing 是三者中最强大和灵活的。在组件类型上,它遵循最大公约数原则。由于Swing可以控制自身GUI系统的全部并有很好的可扩展和灵活性,它几乎可以创建所有你想象得到的组件。唯一的限制是它的AWT容器。在Swing中你还不能跨平台地实现真正的透明化和不规则矩形窗口,因为Swing依赖于AWT顶层容器例如Applet, Window, Frame and Dialog等。除此之外,Swing几乎实现了所有平台上的标准组件。
在组件特征上,Swing遵循最小公倍数原则。它拥有所有平台上可提供的组件特征。不仅如此,你还可以继承已有的Swing组件并添加新的特性。
上面比较主要是在API级别上的。让我们将比较的焦点转移到实现细节上。AWT,SWT和Swing的区别是Swing是纯Java实现,而SWT和AWT 是Java和JNI的混合。当然,它们的目标都是相同的,提供一个跨平台的APIs。然而为了达到这一点,SWT和AWT不得不牺牲一些组件和特性以提供一个通用的APIs。
AWT
一个AWT组件通常是一个包含了对等体接口类型引用的组件类。这个引用指向本地对等体实现。举java.awt.Label为例,它的对等体接口是 LabelPeer。LabelPeer是平台无关的。在不同平台上,AWT提供不同的对等体类来实现LabelPeer。在Windows上,对等体类是WlabelPeer,它调用JNI来实现label的功能。这些JNI方法用C或C++编写。它们关联一个本地的label,真正的行为都在这里发生。作为整体,AWT组件由AWT组件类和AWT对等体提供了一个全局公用的API给应用程序使用。一个组件类和它的对等体接口是平台无关的。底层的对等体类和JNI代码是平台相关的。
SWT
SWT也使用JNI的方法论来实现。但细节不同于AWT。SWT的拥护者听到人们拿SWT和AWT相提并论可是会很生气的,Steve Northover,SWT之父,就曾为此抱怨过。
没错,它们是不同的。让我们深究SWT的代码。在SWT中,各个平台上唯一相同的部分是组件的接口,是类和方法的定义签名。所有的底层代码都是平台差异的。 SWT为每个平台提供了OS类。这个类用JNI封装了许多本地APIs。SWT组件类通过把这些JNI方法黏合在一起提供一个有意义的功能。
例如,在Windows上,文本域的选择是由一个系统调用处理的。这个系统调用在Windows的OS类中作为一个本地方法实现。所以在Windows平台的Text的setSelection方法中只用到了一个JNI调用。
然而,在motif上,文本域的选择包含两个本地调用。SWT就在motif的OS类中实现了两个调用。所以在motif上组件类需要作两次调用来实现文本的选择。
现在你应该能看出SWT和AWT的最大不同了,它们使用了不同的对等体编程方式来消除平台差异。SWT用java代码或有JNI实现的java对等体来黏合系统调用。而AWT把代码包含在对等体中,使情况复杂化了,我个人觉得SWT的方法更加明智。
SWING
到了Swing这里,一切就变得清晰和直接了。除了顶层容器,Swing的实现不依赖于具体平台。它掌管了所有的控制和资源。Swing所需要的是事件输入来驱动系统,以及承接自顶层AWT容器的图形处理,字体和颜色。普通的Swing组件可以看作是AWT容器的一块逻辑区域。它们并没有注册对等体。所有添加到同一顶层容器的Swing组件共享它的AWT对等体以获取系统资源,如字体,图形处理等。Swing将组件自己的数据结构存储在JVM的空间中。它完全由自己管理画图处理,事件分发和组件布局。
由于AWT和SWT都持有对本地组件的引用,它们必须以正确的方式释放这些引用以避免内存泄露和JVM崩溃。AWT将绝大多数资源管理任务交给系统,将开发者从单调乏味的资源管理中解救出来。然而这使得AWT的实现复杂化了。一旦它实现了,开发者很少有机会犯错误并使他们的程序崩溃。
SWT 用的是另一种方法。大体上,SWT让开发者自己来管理资源。它的一条著名的规则是:谁创建,谁释放。因此开发者必须谨慎地显式调用dispose方法释放每一个由他创建的组件和资源。这简化了SWT的实现模型,但把开发者摆在了因错误编码而易于造成程序崩溃这一风险之上。
模拟方式的区别
SWT和Swing在它们的实现上都使用了模拟。SWT只模拟平台上缺失的组件。区别是SWT的模拟更像是AWT的Canvas实现的模拟。SWT的 Composite类有它自己在操作系统中相应的对等体。它从自己的对等体中获得所有它所需要的资源如图形处理的对象,字体和颜色等。它直接从操作系统获取所有的事件并进行处理。然而,Swing组件在操作系统中没有相应的对等体。它只是一块顶层容器中的逻辑区域,实际上它从顶层容器的对等体中借用资源。 Swing的事件并不是底层系统产生的事件。它们实际是由顶层容器处理AWT事件所产生的伪事件。我们会在稍后的事件部分中详细介绍它。
图形层结构
另一个不同之处是Swing组件的z-order系统是来自于AWT组件的。如上所述,Swing组件与顶层AWT容器共享一个对等体。因此,Swing组件也和顶层容器有相同的z-order。SWT和AWT组件都有不同于顶层容器的z-order,通常是高于顶层容器。故而如果AWT组件和Swing组件混合在一起的话,Swing组件将可能被AWT组件遮住。当操作系统开始更新UI的时候,顶层容器和Swing组件总是先于AWT组件绘制。当它们完成绘制,AWT组件会覆盖Swing可能绘制过的地方。因此不提倡Swing和AWT组件的混用。如果有一个浮动的Swing组件如菜单,AWT组件很可能遮盖菜单。以上是AWT,SWT和Swing的区别的介绍