Java语言
声望和它在桌面应用程序(GUI程序)所取得
成就显然极不相符,至今仍然很少能看到非常成功Java桌面程序。虽然有JBuilder,Netbean,JProbe等大型软件作为代表,但这仍不能证明Java
GUI程序是成功
:它们
外观总是和同一操作系统平台下
其它软件显得格格不入。对机器配置
需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC
程序员们所容忍,或是那些不在乎金钱和时间
专业用户所接受。对绝大多数计算机使用者来说,AWT或SWING代表着怪异
界面和无法接受
速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦
终结者,广大Java程序员终于可以开发出高效率
GUI程序,它们拥有标准
外观,几乎没有人能看出你
程序是用Java写出来
,更为重要
是,这些程序是跨平台
。
SWT本身仅仅是Eclipse组织为了开发Eclipse IDE环境所编写
一组底层图形界面 API。或许是无心插柳,或是有意为之,至今为止,SWT无论是在性能和外观上,都超越了SUN公司提供
AWT和SWING。目前Eclipse IDE已经开发到了2.1版本,SWT已经十分稳定。这里指
稳定应该包含两层意思: 字串1
一是指性能上
稳定,其中
关键是源于SWT
设计理念。SWT最大化了操作系统
图形构件API,就是说只要操作系统提供了相应图形
构件,那么SWT只是简单应用JNI技术调用它们,只有那些操作系统中不提供
构件,SWT才自己去做一个模拟
实现。可以看出SWT
性能上
稳定大多时候取决于相应操作系统图形构件
稳定性。
另一个稳定是指SWT API包中
类、方法
名称和结构已经少有改变,程序员不用担心由于Eclipse组织开发进度很快(Eclipse IDE每天都会有一个Nightly版本
发布),而导致自己
程序代码变化过大。从一个版本
SWT更新至另一版本,通常只需要简单将SWT包换掉就可以了。 字串4
第一个SWT程序
字串6
下面让我们开始一个SWT程序。(注意:以下
例子和说明主要针对Windows平台,其它
操作系统应该大同小异)。首先要在Eclipse安装文件中找到SWT包,Eclipse组织并不提供单独
SWT包下载,必须下载完整
Eclipse开发环境才能得到SWT包。SWT是作为Eclipse开发环境
一个插件形式存在,可以在${你
eclipse安装路径}plugins路径下
众多子目录下去搜索SWT.JAR文件,在找到
JAR文件中包含了SWT全部
Java类文件。因为SWT应用了JNI技术,因此同时也要找到相对应
JNI本地化库文件,由于版本和操作平台
不同,本地化库文件
名称会有些差别,比如SWT-WIN32-2116.DLL是Window平台下Eclipse Build 2116
动态库,而在Unix平台相应版本
库文件
扩展名应该是.so,等等。注意
是,Eclipse是一个开放源代码
项目,因此你也可以在这些目录中找到SWT
源代码,相信这会对开发很有帮助。下面是一段打开空窗口
代码(只有main方法)。
字串8
字串7
import com.e2one.example;
public class OpenShell{
public static void main(String [] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.open();
// 开始事件处理循环,直到用户关闭窗口
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
} 确信在CLASSPATH中包括了SWT.JAR文件,先用Javac编译例子程序。编译无错后可运行java -Djava.library.path=${你
SWT本地库文件所在路径} com.e2one.example.OpenShell,比如SWT-WIN32-2116.DLL件所在
路径是C:swtlib,运行
命令应该是java -Djava.library.path=c:swtlib com.e2one.example.OpenShell。成功运行后,系统会打开了一个空
窗口。 字串3
剖析SWT API
字串5
下面再让我们进一步分析SWT API
组成。所有
SWT类都用org.eclipse.swt做为包
前缀,下面为了简化说明,我们用*号代表前缀org.eclipse.swt,比如*.widgets包,代表
是org.eclipse.swt.widgets包。 字串8
我们最常用
图形构件基本都被包括在*.widgets包中,比如Button,Combo,Text,Label,Sash,Table等等。其中两个最重要
构件当数Shell和Composite。Shell相当于应用程序
主窗口框架,上面
例子代码中就是应用Shell构件打开一个空窗口。Composite相当于SWING中
Panel对象,充当着构件容器
角色,当我们想在一个窗口中加入一些构件时,最
到使用Composite作为其它构件
容器,然后再去*.layout包找出一种合适
布局方式。SWT对构件
布局也采用了SWING或AWT中Layout和Layout Data结合
方式,在*.layout包中可以找到四种Layout和与它们相对应
布局结构对象(Layout Data)。在*.custom包中,包含了对一些基本图形构件
扩展,比如其中
CLabel,就是对标准Label构件
扩展,上面可以同时加入文字和图片,也可以加边框。StyledText是Text构件
扩展,它提供了丰富
文本功能,比如对某段文字
背景色、前景色或字体
设置。在*.custom包中也可找到一个新
StackLayout布局方式。 字串6
SWT对用户操作
响应,比如鼠标或键盘事件,也是采用了AWT和SWING中
Observer模式,在*.event包中可以找到事件监听
Listener接口和相应
事件对象,例如常用
鼠标事件监听接口MouseListener,MouseMoveListener和MouseTrackListener,及对应
事件对象MouseEvent。
字串9
*.graphics包中可以找到针对图片、光标、字体或绘图
API。比如可通过Image类调用系统中不同类型
图片文件。通过GC类实现对图片、构件或显示器
绘图功能。 字串6
对不同平台,Eclipse还开发了一些富有针对性
API。例如,在Windows平台,可以通过*.ole.win32包很容易
调用ole控件,这使Java程序内嵌IE浏览器或Word、Excel等程序成为可能!
更复杂
程序 字串1
下面让我们展示一个比上面例子更加复杂一些
程序。这个程序拥有一个文本框和一个按键,当用户点击按键
时候,文本框显示一句欢迎信息。
为了文本框和按键有比较合理
大小和布局,这里采用了GradLayout布局方式。这种布局是SWT中最常用也是最强大
布局方式,几乎所有
格式都可能通过GradLayout去达到。下面
程序也涉及到了如何应用系统资源(Color),以及如何释放系统资源。
字串9
private void initShell(Shell shell) {
//为Shell设置布局对象
GridLayout gShellLay = new GridLayout();
shell.setLayout(gShellLay);
//构造一个Composite构件作为文本框和按键
容器
Composite panel = new Composite(shell,SWT.NONE);
//为Panel指定一个布局结构对象。
这里让Panel尽可能
占满Shell,
也就是全部应用程序窗口
空间。
GridData gPanelData = new GridData(GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
panel.setLayoutData(gPanelData);
//为Panel也设置一个布局对象。文本框和按键将按这个布局对象来显示。
GridLayout gPanelLay = new GridLayout();
panel.setLayout(gPanelLay);
//为Panel生成一个背景色
final Color bkColor = new Color(Display.getCurrent(),200,0,200);
panel.setBackground(bkColor);
//生成文本框
final Text text = new Text(panel,SWT.MULTI|SWT.WRAP);
//为文本框指定一个布局结构对象, 字串7
这里让文本框尽可能
占满Panel
空间。
GridData gTextData = new GridData (GridData.GRAB_HORIZONTAL| GridData.GRAB_VERTICAL|GridData.FILL_BOTH);
text.setLayoutData(gTextData);
//生成按键
Button butt = new Button(panel,SWT.PUSH);
butt.setText("Push");
//为按键指定鼠标事件
butt.addMouseListener(new MouseAdapter(){
public void mouseDown(MouseEvent e){
//当用户点击按键
时候,显示信息
text.setText("Hello SWT");
}
});
//当主窗口关闭时,会触发DisposeListener。这里用来释放Panel
背景色。
shell.addDisposeListener(new DisposeListener(){
public void widgetDisposed(DisposeEvent e) {
bkColor.dispose();
}
});
} 把这段代码中
方法initShell()加入到第一个打开空窗口
例子中,得到
是一段能成功运行
完整GUI应用程序。运行方法可参考第一个例子。
字串4
系统资源
管理 字串2
在一个图形化
操作系统中开发程序,都要调用系统中
资源,如图片、字体、颜色等。通常这些资源都是有限
,程序员务必非常小心
使用这些资源:当不再使用它们时,就请尽快释放,不然操作系统迟早会油尽灯枯,不得不重新启动,更严重
会导致系统崩溃。 字串1
SWT是用Java开发
,Java语言本身
一大优势就是JVM
"垃圾回收机制",程序员通常不用理会变量
释放,内存
回收等问题。那么对SWT而言,系统资源
操作是不是也是如此?答案是一个坏消息,一个
消息。
坏消息是SWT并没采用JVM
垃圾回收机制去处理操作系统
资源回收问题,一个关键
因素是因为JVM
垃圾回收机制是不可控
,也就是说程序员不能知道,也不可能做到在某一时刻让JVM回收资源!这对系统资源
处理是致命
,试想你
程序希望在一个循环语句中去查看数万张图片,常规
处理方式是每次调入一张,查看,然后就立即释放该图片资源,而后在循环调入下一张图片,这对操作系统而言,任何时刻程序占用
仅仅是一张图片
资源。但如果这个过程完全交给JVM去处理,也许会是在循环语句结束后,JVM才会去释放图片资源,其结果可能是你
程序还没有运行结束,操作系统已经宕掉。
字串6
但下面
消息也许会让这个坏消息变得无关紧要。对于SWT,只需了解两条简单
"黄金"法则就可以放心
使用系统资源!之所以称为黄金法则,一是因为少,只有两条,二是因为它们出奇
简单。第一条是"谁占用,谁释放",第二条是"父构件被销毁,子构件也同时被销毁"。第一条原则是一个无任何例外
原则,只要程序调用了系统资源类
构造函数,程序就应该关心在某一时刻要释放这个系统资源。比如调用了
字串9
Font font = new Font (display, "Courier", 10, SWT.NORMAL); 那么就应该在不在需要这个Font
时候调用 字串1
font.dispose(); 对于第二个原则,是指如果程序调用某一构件
dispose()方法,那么所有这个构件
子构件也会被自动调用dispose()方法而销毁。通常这里指
子构件与父构件
关系是在调用构件
构造函数时形成
。比如,
字串5
Shell shell = new Shell();
Composite parent = new Composite(shell,SWT.NULL);
Composite child = new Composite(parent,SWT.NULL); 其中parent
父构件是shell,而shell则是程序
主窗口,所以没有相应
父构件,同时parent又包括了child子构件。如果调用shell.dispose()方法,应用第二条法则,那么parent和child构件
dispose()方法也会被SWT API自动调用,它们也随之销毁。 字串4
字串7
线程问题 字串2
在任何操作平台
GUI系统中,对构件或一些图形API
访问操作都要被严格同步并串行化。例如,在一个图形界面中
按键构件可被设成可用状态(enable)或禁用状态(disable),正常
处理方式是,用户对按键状态设置操作都要被放入到GUI系统
事件处理队列中(这意味着访问操作被串行化),然后依次处理(这意味着访问操作被同步)。想象当按键可用状态
设置函数还没有执行结束
时候,程序就希望再设置该按键为禁用状态,势必会引起冲突。实际上,这种操作在任何GUI系统都会触发异常。
Java语言本身就提供了多线程机制,这种机制对GUI编程来说是不利
,它不能保证图形构件操作
同步与串行化。SWT采用了一种简单而直接
方式去适应本地GUI系统对线程
要求:在SWT中,通常存在一个被称为"用户线程"
唯一线程,只有在这个线程中才能调用对构件或某些图形API
访问操作。如果在非用户线程中程序直接调用这些访问操作,那么SWTExcepiton异常会被抛出。但是SWT也在*.widget.Display类中提供了两个方法可以间接
在非用户线程
进行图形构件
访问操作,这是通过
syncExec(Runnable)和asyncExec(Runnable)这两个方法去实现
。例如:
字串2
//此时程序运行在一个非用户线程中,并且希望在构件panel上加入一个按键。
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
Button butt = new Button(panel,SWT.PUSH);
butt.setText("Push");
}
}); 方法syncExec()和asyncExec()
区别在于前者要在指定
线程执行结束后才返回,而后者则无论指定
线程是否执行都会立即返回到当前线程。
SWT
扩展:JFace
JFace与SWT
关系
比Microsoft
MFC与SDK
关系,JFace是基于SWT开发,其API比SWT更加易于使用,但功能却没SWT来
直接。比如下面
代码应用JFace中
MessageDialog打开一个警告对话框:
MessageDialog.openWarning(parent,"Warning","Warning message");如果只用SWT完成以上功能,语句不会少于30行! 字串2
JFace原本是为更加方便
使用SWT而编写
一组API,其主要目
是为了开发Eclipse IDE环境,而不是为了应用到其它
独立应用程序。因此在Eclipse 2.1版本之前,很难将JFace API完整
从Eclipse
内核API中剥离出来,总是要多多少少导入一些非JFace以外
Eclipse核心代码类或接口才能得到一个没有任何编译错误
JFace开发包。但目前Eclipse组织似乎已经逐渐意识到了JFace在开发独立应用程序起到
重要作用,在正在开发
2.1版本中,JFace也开始变成了和SWT一样
完整独立
开发包,只是这个开发包还在变动中(笔者写本文时,应用
Eclipse2.1M3版本)。JFace开发包
包前缀是以org.eclipse.jface开头。JAR包和源代码也和SWT一样,也在${你
eclipse安装路径}plugins路径下去找。
对开发人员来说,在开发一个图形构件
时候,比较
方式是先到JFace包去找一找,看是不是有更简洁
实现方法,如果没有再用SWT包去自己实现。比如JFace为对话框提供了很
支持,除了各种类型
对话框(比如上面用
MessageDialog,或是带有Title栏
对话框),如要实现一个自定义
对话框也最
从JFace中
Dialog类继承,而不是从SWT中
*.widget.Dialog继承。 字串5
应用JFace中
Preference包中
类很容易为自己
软件做出一个很专业
配置对话框。对于Tree、Table等图形构件,它们在显示
同时也要和数据关联,例如Table中显示
数据,在JFace中
View包中为此类构件提供了Model-View方式
编程方法,这种方法使显示与数据分开,更加利于开发与维护。JFace中提供最多
功能就是对文本内容
处理。可以在org.eclipse.jface.text.*包中找到数十个与文本处理相关类。 字串3
与应用程序更近一步 字串3
Java程序通常是以class文件
方式发布
,运行class需要JRE或JDK
支持。这又是Java GUI程序
另一个致命
弱点,想象对一个面向广大用户
应用程序来说,无论你
程序功能有多简单,或是你
代码十分
精简,你都不得不让用户去下载一个7、8M
JRE,那是多么令人沮丧
一件事。而且对程序员来说,Class通常意味着源代码
暴露,反编译
工具让那些居心叵测
人轻易得到你
源代码。虽然有很多对Class
加密方法,但那总是以牺牲性能为代价
。
在我们还有其它
方式可用:把Class编译成exe文件!
通过SWT开发包,简单、跨平台、可靠等这些Java语言本身所具有
优点正渐渐融合到图形界面
应用程序开发中去。因此,我相信Java语言
另一扇成功之门正在逐渐打开。
字串8
字串7
![我要研发网[www.51dev.com]](/templets/images/toplogo.gif)
