3.1.3 Widget类
Widget既是SWT中所有窗口组件的抽象基类,也是SWT中一个非常重要的概念。在SWT应用程序中,窗口组件是用户与程序交互的接口,它是信息的载体,它将程序的数据呈现在图形化的窗口中,同时它自身包含一定的状态,在不同的状态下,其自身的显示也会有所不同;窗口组件还用于监听用户事件,它会根据鼠标和键盘事件改变其状态。
SWT使用平台窗口组件,因此当创建一个SWT
Widget的对象时,与操作系统相关的组件将会被创建。当这些SWT对象被销毁时,相应的平台组件也会被释放。
1.创建Widget
表3-1列出了Widget类的构造函数。
表3‑1 Widget类的构造函数
构 造 函 数
描 述
Widget(Widget parent, int style)
创建窗口组件对象,该组件的父亲为parent,该组件的行为和外观由style决定
Widget类是一个抽象类,它不能够被实例化,但是大部分SWT窗口组件类都继承自该类,因此这里所说的Widget类的创建时特性适用于它的子类。在大多数情况下,创建一个Widget对象需要传递一个父Widget对象,该父亲窗口组件对其孩子组件的生命周期具有一定的控制,当父亲组件消亡时,其孩子组件也消亡。不同的组件类其允许的父亲组件有所不同,比如TabItem的父亲必须是TabFolder,TreeItem的父亲可以是Tree或者TreeItem。
创建Widget实例对象时需要传递的另一个参数是style,该参数限定了组件的行为和外观,比如在创建Tree组件时,可以使用SWT.SINGLE参数,该参数说明创建的树组件中不能够同时选中多个元素;当然也可以使用SWT.
MULTI参数。
在创建Widget对象时,样式参数可以是单个值,也可以是多个样式的组合。比如在创建Tree组件时,可以使用SWT.SINGLE和SWT.CHECK的组合,后者表示树中的每一个元素都有一个勾选框。当然,读者在创建Tree组件时可以传入SWT.MULTI
| SWT.SINGLE,虽然这在逻辑上不成立,但是不会有任何编译错误。在样式矛盾的情况下,树组件会使用SWT.SINGLE。
所有的样式都以常量的形式定义在org.eclipse.swt.SWT类中。
不同的组件有不同的样式,在SWT的Java Doc中列出了每个SWT
组件所允许的样式。在SWT中,每个组件都能够应用其父亲组件所对应的样式。
2.释放Widget
使用Swing开发图形应用程序,开发人员不需要关心资源的释放,因为针对操作系统的窗口组件,Swing使用Java对象来模拟它们,这样资源的释放就由Java的垃圾回收器来管理了。因为窗口组件在创建时,会从操作系统获得相应的平台组件资源,通常这些资源是非常昂贵的,因此需要迅速释放。为了高效管理这些资源,SWT的设计中要求SWT程序直接管理这些资源,Widget类中提供了一个名为dispose()的方法,在SWT的开发过程中,会常和该方法打交道,该方法用于释放与组件相关的资源。
关于窗口组件的释放,有以下两个准则。
准则1
主动创建它,则主动释放它
这个准则的含义是,当在程序中使用new创建了一个组件对象,就应该调用dispose()方法来释放它。比如在HelloSWT程序的最后,display被释放就是通过调用dispose方法实现的。需要指出的是,display并不继承自Widget类,但是在组件释放的概念上,它们是一致的。
准则2
释放组件时,其孩子组件也会被释放
按照准则1的概念,那么在HelloSWT程序中,也应该调用Shell对象的dispose()方法来释放其孩子组件。其实不然,当释放一个组件时,其孩子组件也会被释放,比如在释放一个Tree组件时,其中的各个元素(TreeItem组件)也会被自动释放,因此不需要主动调用这些孩子组件的dispose()方法。
读者可能会发现这样一个问题,当调用组件的dispose()方法时,与其对应的平台组件将被释放,但是此时该组件对象仍然存在,此时调用该对象的方法会有什么结果呢,如代码3-2所示。
代码 3-2
import
org.eclipse.swt.widgets.Display;
import
org.eclipse.swt.widgets.Shell;
public class HelloSWT
{
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell(display);
shell.setText("Hello, SWT!");
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
shell.setText("it is wrong!");
}
}
运行代码3-2,该程序能够正常启动并显示,但是当关闭程序时,会抛出SWTException异常,如代码3-3所示。
代码
3-3
Exception in thread
"main" org.eclipse.swt.SWTException: Widget is disposed
at org.eclipse.swt.SWT.error(SWT.java:2942)
at org.eclipse.swt.SWT.error(SWT.java:2865)
at org.eclipse.swt.SWT.error(SWT.java:2836)
at org.eclipse.swt.widgets.Widget.error(Widget.java:395)
at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:297)
at org.eclipse.swt.widgets.Shell.open(Shell.java:815)
at first.HelloSWT.main(HelloSWT.java:16)
因此当一个组件的dispose()方法被调用,该组件正常释放之后,就不能够再对该组件对象以及其孩子组件对象进行任何操作了。从理论上来说,SWT的设计所引发的该问题的确不怎么光彩,但是在实际开发中,这个问题非常容易避免。
3.1.4 Display类
虽然Display类包含了众多与窗口组件相似的概念,比如生命周期、事件及其监听等,但是Display并不是Widget类的子类。Display类没有对应的窗口形式,它用于在SWT与窗口平台之间建立起一座桥梁。
Display类将SWT程序中相应的代码转换为操作系统平台的窗口调用,Display类的一个重要功能是维护一组事件监听器,当事件发生时(鼠标事件、键盘事件、选择事件等),Display会从操作系统获得事件队列,并根据事件类型触发相应的事件监听器。这个监听过程体现在readAndDispatch()方法上,如表3-2所示。
表3‑2 类Display的构造函数
方 法
描 述
boolean readAndDispatch()
从窗口系统的事件队列中读取事件,一次一件,触发相应的监听器。如果事件队列中还包含事件,返回true,否则返回false
boolean sleep()
允许用户界面暂时释放CPU资源
任何SWT程序都必须拥有一个Display类的实例,在HelloSWT中,程序的第一步便是创建该对象,当没有事件时,通过调用sleep()方法让该用户界面释放CPU资源。
3.1.5 Shell类
运行HelloSWT程序,可以看到一个标准的窗口程序,这个窗口就是Shell窗口组件。该组件作为容器“存放”应用程序的其他控制组件,比如Label、Text、Tree等,当创建这些控制组件时,通常都以Shell组件作为其父亲组件,它们的关系如图3-10所示。
图3-10
Shell在SWT程序架构中的位置
注意:Widget子类之间的继承关系与创建窗口组件时的父子关系是两个不同的概念。
Shell是一个窗口组件,它也是Widget类的一个子类,因此Shell组件遵从前文描述的有关窗口组件生命周期的概念,Shell类提供了多个构造方法,如表3-3所示。
表3‑3 类Shell的构造函数
构 造 函 数
描 述
Shell()
该构造函数相当于调用Shell(Display(null))。在这种情况下,Shell对象会创建在当前活动的display之上,如果没有活动display,则创建在默认display之上
Shell(Display display)
Shell对象创建在display之上,使用Shell组件的默认样式(SWT.SHELL_TRIM)
Shell(Display display, int style)
使用display和style样式创建Shell对象
Shell(int style)
使用style样式,其他与Shell()构造函数同
Shell(Shell parent)
以parent作为Shell对象的父亲组件,以父亲组件的display作为其display,以SWT.DIALOG_TRIM为样式创建Shell对象
Shell(Shell parent, int style)
采用style样式,其他与Shell(Shell parent)构造函数同
在创建Shell对象时,不提倡使用无Display对象的构造函数,即Shell()和Shell(int
style)。在创建Shell对象时,通常不需要特别指定Shell组件的样式,当没有父亲Shell作为参数时,一个常见的Shell组件将被创建,该组件包含“关闭”、“最大化”、“最小化”按钮,包含一个标题栏,以及具有改变窗口大小的功能。如果构造Shell时包含一个非空的父亲Shell,则创建的Shell样式与通常的对话框样式相似,该窗口包含标题栏、“关闭”按钮以及边框。表3-4是Shell组件的样式列表。
表3‑4 Shell的样式
样 式
描 述
BORDER
窗口使用边框
CLOSE
在窗口右上角添加“关闭”按钮
MIN
在窗口右上角添加“最小化”按钮
MAX
在窗口右上角添加“最大化”按钮
NO_TRIM
窗口没有“特殊样式”,该窗口没有边框,没有“关闭”、“最大化”、“最小化”按钮,不能修改其大小
RESIZE
大小可修改
TITLE
窗口包含标题栏
DIALOG_TRIM
BORDER | CLOSE | TITLE的组合
SHELL_TRIM
CLOSE | MIN | MAX | RESIZE | TITLE的组合
Shell组件还包含一个称做“模式”的样式,该样式用于决定是否阻拦该Shell组件依赖的display上的其他输入。该样式可以是APPLICATION_MODAL、MODELESS、PRIMARY_MODAL和SYSTEM_MODAL。PRIMARY_MODAL样式允许Shell组件阻拦对其父亲组件的输入;APPLICATION_MODAL阻拦Shell组件依赖的display上的所有其他Shell组件的输入;SYTEM_MODAL样式阻拦当前系统中所有的向Shell组件的输入。
代码3-4是使用“模式”样式的一个例子。在这段代码中,创建完shell对象之后,以shell为父亲组件,创建了一个称为shell2的窗口组件。根据上文对构造函数的介绍,shell2也依赖于display对象,同时,当shell释放时,shell2也会得到释放。shell2的样式包含两个组合,前一个决定了shell2外观样式,后一个决定了shell2对输入事件的阻拦机制。
代码 3-4
public class HelloSWT
{
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell(display,
SWT.SHELL_TRIM | SWT.BORDER);
shell.setText("Hello, SWT!");
shell.open ();
Shell shell2 = new Shell(shell, SWT.DIALOG_TRIM |
SWT.PRIMARY_MODAL);
shell2.setText("child shell");
shell2.open();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
}
运行代码3-4,可以看到它在桌面上打开两个窗口组件,上层窗口对应shell2。此时无法单击下层窗口,因为对于下层窗口的所有输入都被shell2对应的窗口阻拦。如果在上面的代码中,将SWT.PRIMARY_MODAL去掉,则这种阻拦方式也就消失了。
注意:不同的平台对阻拦“模式”的支持有所不同,在这种情况下,SWT会进行相应的“向上向下兼容”,比如一些操作系统就不支持SYSTEM_MODAL,此时就会向下兼容,使用APPLICATION_MODAL。
熟悉Shell的这些样式是非常重要的,读者应该多尝试它们。
本文详细介绍了SWT中的Widget类,它是所有窗口组件的基类,负责与操作系统交互。创建Widget对象时,需要指定父组件和样式,如创建Tree组件时可以设置单选或多选样式。Widget类提供了dispose()方法来释放资源,遵循'主动创建则主动释放'的原则。同时,释放组件时,其子组件也会被自动释放。Display类作为SWT与操作系统之间的桥梁,处理事件监听。Shell类作为窗口组件,可以设置多种样式,如BORDER、CLOSE等,并支持不同类型的模态。
733

被折叠的 条评论
为什么被折叠?



