RSS
热门关键字:
当前位置 : 主页>编程开发>java编程>入门>列表

Java桌面应用程序设计新贵:SWT 简介

来源:我要研发网 作者: 时间:1970-01-01 点击:



  Java语言声望和它在桌面应用程序(GUI程序)所取得成就显然极不相符,至今仍然很少能看到非常成功Java桌面程序。虽然有JBuilder,Netbean,JProbe等大型软件作为代表,但这仍不能证明JavaGUI程序是成功:它们外观总是和同一操作系统平台下其它软件显得格格不入。对机器配置需求也似乎永无止境,这使得它们只能被一些总是拥有当前最高性能PC程序员们所容忍,或是那些不在乎金钱和时间专业用户所接受。对绝大多数计算机使用者来说,AWT或SWING代表着怪异界面和无法接受速度。Standard Widget Toolkit(SWT)或许是Java这一噩梦终结者,广大Java程序员终于可以开发出高效率GUI程序,它们拥有标准外观,几乎没有人能看出你程序是用Java写出来,更为重要是,这些程序是跨平台

字串4

  SWT本身仅仅是Eclipse组织为了开发Eclipse IDE环境所编写一组底层图形界面 API。或许是无心插柳,或是有意为之,至今为止,SWT无论是在性能和外观上,都超越了SUN公司提供AWT和SWING。目前Eclipse IDE已经开发到了2.1版本,SWT已经十分稳定。这里指稳定应该包含两层意思: 字串1

  一是指性能上稳定,其中关键是源于SWT设计理念。SWT最大化了操作系统图形构件API,就是说只要操作系统提供了相应图形构件,那么SWT只是简单应用JNI技术调用它们,只有那些操作系统中不提供构件,SWT才自己去做一个模拟实现。可以看出SWT性能上稳定大多时候取决于相应操作系统图形构件稳定性。

字串7

  另一个稳定是指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。

字串3


字串9

  *.graphics包中可以找到针对图片、光标、字体或绘图API。比如可通过Image类调用系统中不同类型图片文件。通过GC类实现对图片、构件或显示器绘图功能。 字串6

  对不同平台,Eclipse还开发了一些富有针对性API。例如,在Windows平台,可以通过*.ole.win32包很容易调用ole控件,这使Java程序内嵌IE浏览器或Word、Excel等程序成为可能!

字串1

  更复杂程序 字串1

  下面让我们展示一个比上面例子更加复杂一些程序。这个程序拥有一个文本框和一个按键,当用户点击按键时候,文本框显示一句欢迎信息。

字串8

  为了文本框和按键有比较合理大小和布局,这里采用了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


字串9

  系统资源管理 字串2

  在一个图形化操作系统中开发程序,都要调用系统中资源,如图片、字体、颜色等。通常这些资源都是有限,程序员务必非常小心使用这些资源:当不再使用它们时,就请尽快释放,不然操作系统迟早会油尽灯枯,不得不重新启动,更严重会导致系统崩溃。 字串1

  SWT是用Java开发,Java语言本身一大优势就是JVM"垃圾回收机制",程序员通常不用理会变量释放,内存回收等问题。那么对SWT而言,系统资源操作是不是也是如此?答案是一个坏消息,一个消息。

字串8

  坏消息是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系统都会触发异常。

字串1

  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()区别在于前者要在指定线程执行结束后才返回,而后者则无论指定线程是否执行都会立即返回到当前线程。

字串4

  SWT扩展:JFace

字串7

  JFace与SWT关系比MicrosoftMFC与SDK关系,JFace是基于SWT开发,其API比SWT更加易于使用,但功能却没SWT来直接。比如下面代码应用JFace中MessageDialog打开一个警告对话框:

字串5

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路径下去找。

字串8


字串2

  对开发人员来说,在开发一个图形构件时候,比较方式是先到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、8MJRE,那是多么令人沮丧一件事。而且对程序员来说,Class通常意味着源代码暴露,反编译工具让那些居心叵测人轻易得到你源代码。虽然有很多对Class加密方法,但那总是以牺牲性能为代价在我们还有其它方式可用:把Class编译成exe文件!

字串7

  通过SWT开发包,简单、跨平台、可靠等这些Java语言本身所具有优点正渐渐融合到图形界面应用程序开发中去。因此,我相信Java语言另一扇成功之门正在逐渐打开。

字串8

字串7

最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册
相关文章