Java桌面应用程序设计:SWT简介
首先是指性能的稳定性,其关键是来自SWT的设计理念,将操作系统的图形化组件API最大化。也就是说,只要操作系统提供了相应的图形组件,SWT就简单地通过JNI技术调用它们,只对那些操作系统中没有提供的组件做一个模拟。可以看出,SWT性能的稳定性在大多数情况下取决于相应图形组件的稳定性。
另一个稳定性意味着SWT API包中的类方法的名称和结构很少改变。程序员不用担心自己的程序代码因为Eclipse的快速发展而改变太多(Eclipse IDE每天都会发布一个版本的Nightly)。从一个版本的SWT升级到另一个版本通常只需要替换SWT软件包。
第一个SWT计划
我们来启动一个SWT程序(注意下面的例子和说明主要是针对Windows平台的,其他操作系统应该也差不多)。首先,我们应该在Eclipse安装文件中找到SWT包。Eclipse组织不提供单独的SWT包下载,我们必须下载完整的Eclipse开发环境才能获得SWT包。SWT是Eclipse开发环境的一个插件。组件表单可以在${您的eclipse安装路径} \ plugins路径下的许多子目录中搜索SWT JAR文件。找到的JAR文件包含了SWT所有的Java类文件。因为SWT应用了JNI技术,所以还需要找到相应的JNI本地化库文件。由于版本和操作平台的不同,本地化库文件的名称会有些不同。例如,SWT WIN DLL是Eclipse windows平台下的。Build的动态库,Unix平台上对应版本库文件的扩展应该是这样的等等。注意,Eclipse是一个开源项目,所以你也可以在这些目录中找到SWT的源代码,相信对开发很有帮助。下面是打开一个空窗口的代码(只有main方法)。
导入e一个例子;public类OpenShell { public static void main(String[]args){ Display Display = new Display();Shell shell =新壳(显示);shell open();//开始事件处理循环,直到用户关闭窗口。shell isDisposed()) { if(!display readAndDispatch())显示sleep();} dispose();}}
确保SWT JAR文件包含在类路径中。先用Javac编译示例程序,然后运行java Djava库path=${你的SWT本地库文件所在的路径} e一个示例OpenShell比如SWT WIN DLL所在的路径是c: \ swtlib运行的命令应该是Java DJ ava库path = c: \ swtlib e一个示例open shell。成功运行后,系统会打开一个空窗口。
分析SWT API
让我们进一步分析SWT API的构成。所有SWT类都使用eclipse swt作为包的前缀。为了简化解释,我们使用*来表示前缀eclipse swt。例如,* widgets包代表eclipse swt widgets包。
我们最常用的图形组件基本都包含在widgets包中最重要的两个组件,比如Button Combo Text Label Sash Table,是Shell和Composite Shell,相当于应用程序的主窗口框架。在上面的示例代码中,应用Shell组件打开一个空窗口,Composite相当于SWING中的Panel对象。当我们要给一个窗口添加一些组件时,最好使用Composite作为其他组件的容器,然后到* Layout包中寻找合适的布局方法。SWT在SWING或AWT中也采用布局和布局数据相结合的方式。四种布局及其相应的布局结构对象(布局数据)可在布局包中找到。自定义包包含了对一些基本图形组件的扩展,比如CLabel是对标准标签组件的扩展,可以同时向其中添加文本和图片,StyledText是对文本组件的扩展,提供了丰富的文本功能,比如设置一段文本的背景色、前景色或字体,在*自定义包中还可以找到新的StackLayout模式。
SWT对用户操作的响应,比如鼠标或键盘事件,也采用AWT和SWING中的观察者模式。在* event包中,可以找到事件监控的监听器接口和对应的事件对象,比如常用的MouseEvent监控接口MouseListener MouseMoveListener和MouseTrackListener以及对应的事件对象MouseEvent。
*图形包可以找到图片光标字体或者画图的API,比如系统中不同类型的图片文件可以通过Image类调用,图片组件或者显示器的画图功能可以通过GC类实现。
Eclipse还为不同的平台开发了一些有针对性的API。比如在Windows平台上,通过* ole win包可以轻松调用ole控件,使得在Java程序中嵌入IE浏览器或者Word Excel成为可能!
更复杂的程序
让我们展示一个比上面的例子更复杂的程序。这个程序有一个文本框和一个按钮。当用户单击该按钮时,文本框会显示一条欢迎消息。
为了文本框和按钮有一个合理的大小和布局,这里采用的是GridLayout布局方法,这是SWT最常用也是最强大的布局方法。几乎所有格式都可以通过GridLayout实现。下面的过程还涉及到如何应用系统资源(颜色)以及如何释放系统资源。
private void init Shell(Shell Shell){//为Shell设置布局对象Gridlayoutgshelllay = newGridlayout();shell set layout(gShellLay);//构造一个复合组件作为文本框和按键的容器Composite Panel = New Composite(Shell SWT None);//指定面板的布局结构对象。这里让面板尽可能的占据外壳的空间,也就是所有的应用窗口。grid data gpaneldata = New grid data(grid data Grab _ Horizontal | grid data Grab _ Vertical | grid data Fill _ Both);面板setLayoutData(gPanelData);//为面板设置布局对象。文本框和按钮将根据此布局对象显示gridlayoutgpanellay = newgridlayout();面板设置布局(gPanelLay);//为面板生成一个背景色:final color bk color = new color(display get current());面板设置背景(bk color);//生成文本框final text = new text(panel swt multi | swt wrap);//指定文本框的布局结构对象。让文本框尽可能多的占据面板的空间。grid data gtextdata = New grid data(grid data Grab _ Horizontal | grid data Grab _ Vertical | grid data Fill _ Both);text setLayoutData(gTextData);//Generate button button = new button(面板swt push);对接setText(推);//指定鼠标事件Butt addmouse listener(New Mouse adapter(){ Public Void MouseDown(Mouse Event E){//用户点击按键时显示消息文本setText(Hello SWT);} };//当主窗口关闭时,会触发DisposeListener。背景色shell addDisposeListener(new DisposeListener(){ public void widget disposed(dispose event e){ bkcolorDispose();} };}
将这段代码中的initShell()方法添加到第一个打开空窗口的例子中,就可以得到一个完整的可以成功运行的GUI应用程序。操作方法请参考第一个例子。
系统资源的管理
在图形操作系统中,开发人员必须调用系统中的资源,如图片、字体和颜色。通常,这些资源是有限的。程序员必须非常小心地使用这些资源。当它们不再被使用时,请尽快释放它们,否则操作系统迟早会油尽而不得不重启。更严重的是,会导致系统崩溃。SWT是用Java开发的,Java语言本身的一大优势就是JVM。垃圾收集机制程序员通常不用关注变量的释放和内存的回收。那么系统资源的操作对SWT来说是一样的吗?答案是坏消息和好消息。
坏消息是SWT没有使用JVM的垃圾收集机制来处理操作系统的资源回收。一个关键因素是JVM的垃圾回收机制是不可控的,也就是说程序员无法知道也无法让JVM在某个时刻回收资源!这对系统资源的处理是致命的。假设你的程序想在一个循环语句中查看数万张图片。常规的处理方法是一次调入一张图片然后立即释放图片资源,然后循环调入下一张图片。对于操作系统来说,程序在任何时候都只占用一个图片资源,但是如果这个过程完全由JVM来处理,可能是在循环语句结束之后,JVM才会释放图片资源。结果可能是你的程序还没有运行,操作系统就崩溃了。
但接下来的好消息可能会让这个坏消息变得无关紧要。对于SWT,您可以通过了解两条简单的黄金法则来安全地使用系统资源!它被称为黄金法则,因为只有两条,也因为它们出奇的简单。第一条规则是,谁占有它,谁就释放它。第二个规则是父组件被销毁,子组件同时被销毁。第一条规则是没有任何例外的原则。只要程序调用系统资源类的构造函数,程序就要关心在某个时刻释放系统资源,比如调用。
Font font =新字体(显示快递SWT正常);
那你不需要的时候就应该调用这个字体
font dispose();
对于第二个原则,如果程序调用一个组件的dispose()方法,那么这个组件的所有子组件都会被自动调用dispose()方法并销毁。通常,子组件和父组件之间的关系是在调用组件的构造函数时形成的,例如。
Shell Shell = new Shell();复合父代=新复合(shell SWT NULL);复合子代=新复合(父SWT为空);
其中,父组件是外壳,外壳是程序的主窗口,所以没有对应的父组件。同时,父组件还包括子组件。如果通过调用shell dispose()方法来应用第二个规则,父组件和子组件的dispose()方法也将被SWT API自动调用,并且它们也将被销毁。
线程问题
在任何操作平台的GUI系统中,对组件或某些图形API的访问操作必须严格同步和序列化。例如,图形界面中的关键组件可以被设置为启用状态或禁用状态。通常的处理方法是,用户将所有的键状态设置操作放入GUI系统的事件处理队列中(这意味着访问操作是序列化的),然后依次处理它们(这意味着访问操作是同步的)。假设在设置键的可用状态的功能完成之前,程序想要将键设置为禁用状态。事实上,这种操作在任何GUI系统中都不可避免地会导致冲突。
Java语言本身提供了多线程机制,不利于GUI编程。它不能保证图形组件操作的同步和序列化。SWT采用一种简单直接的方式来满足本地GUI系统对线程的要求。在SWT中,通常有一个唯一的线程叫做用户线程,只有在这个线程中才能访问组件或者调用一些图形API。如果是在非用户行,进程中的程序直接调用这些访问操作,那么就会抛出SWTExcepiton异常。但是,SWT也在* widget Display类中提供了两个方法,可以间接访问非用户线程中的图形组件。这是通过两种方法实现的:syncExec(Runnable)和asyncExec(Runnable)。举个例子,
//此时程序正在非用户线程中运行,想要在组件面板中添加一个键。
display get current()async exec(new Runnable(){ public void run(){ Button butt = new Button(面板SWT推送));对接setText(推);}});
syncExec()和asyncExec()的区别在于,前者会在指定线程执行后返回,而后者无论指定线程是否执行,都会立即返回当前线程。
SWT的扩展JFace
JFace和SWT的关系就像微软的MFC和SDK的关系。JFace是基于SWT开发的,API比SWT好用,但功能没有SWT直接。例如,以下代码通过在JFace中应用MessageDialog来打开一个警告对话框。
MessageDialog openWarning(父警告警告消息);
如果你只用SWT来完成上面的函数语句,那就不会少于行!
JFace最初是作为一组API编写的,以便更方便地使用SWT。它的主要目的是开发Eclipse IDE环境,而不是将其应用于其他独立的应用程序。所以在Eclipse版本之前,很难将JFace API与Eclipse内核API完全分离,总是导入一些非JFace的Eclipse内核。只有代码类或接口才能得到一个没有任何编译错误的JFace开发包,但目前Eclipse组织似乎已经逐渐意识到JFace在开发独立应用程序中的重要作用。在开发版本中,JFace也开始像SWT一样成为一个完整独立的开发包,但这个开发包还在变化(我写这篇文章时应用的是Eclipse M版本)。JFace开发包的包前缀以eclipse jface开头,JAR包的源代码也和SWT一样在$ {你的eclipse安装路径} \ plugins路径下。
对于开发者来说,开发一个图形化组件更好的方法是去JFace包里找找有没有更简洁的实现方法。如果不用SWT包自己实现,JFace对对话框提供了很好的支持,除了各种类型的对话框(比如上面用的MessageDialog或者带Title列的对话框),如果要实现自定义对话框,最好从JFace中的Dialog类继承,而不是从SWT的* widget对话框继承。
通过使用JFace中Preference包中的类,很容易为自己的软件制作一个专业的配置对话框。对于树表等图形组件,应该在显示的同时与数据相关联。比如表格中显示的数据,在JFace的视图包中为这类组件提供了模型视图的编程方法,使得显示和数据更加分离。有利于JFace的开发和维护。提供最多的功能是处理文本内容。在eclipse jface text *包中可以找到几十个与文本处理相关的类。
离应用更近了一步
Java程序通常以类文件的形式发布。运行类需要JRE或JDK的支持,这是Java GUI程序的另一个致命弱点。试想一下,对于一个面向大量用户的应用,无论你的程序功能多么简单,代码多么精简,你都要让用户下载一个M JRE。对于程序员来说是一件多么令人沮丧的事情。一般来说,类通常意味着源代码的暴露,反编译工具很容易让别有用心的人拿到你的源代码。虽然类的加密方法很多,但总是以牺牲性能为代价。幸运的是,还有其他方法可以将Class编译成exe文件!
Lishi Xinzhi/Article/program/Java/gj/201311/27737