【IT168 技术文章】无论您使用什么语言开发 GUI 应用程序,收集用户输入的交互式机制都是不可或缺的。属于开发中的 Eclipse 项目的 JFace 工具箱提供了一组功能强大的用户界面组件,开发人员可以轻松地在独立应用程序中利用这些组件。JFace 工具箱中比较有趣的组件之一是对向导的支持。JFace 向导和标准窗口小部件工具箱(Standard Widget Toolkit,SWT)内的其它用户界面组件结合起来提供了灵活的机制,以便系统化地收集用户输入和执行数据验证。
过去,用 Java 开发高度交互式的客户机端应用程序,使这些应用程序看起来就象本机应用程序并且能象本机应用程序那样操作,这样的想法就象圣杯(Holy Grail)一样难以捉摸。随着 Eclipse 项目的出现,开发人员开始创建模拟原汁原味的本机应用程序的 Java 应用程序。虽然传统意义上认为 Eclipse 项目是 Java 的开放源码集成开发环境(IDE),但是该项目涉及的范围要广得多。事实上,在 eclipse.org 网站首页上,Eclipse 被描述为“…一种通用工具平台 - 是一种开放的可扩展 IDE,它可以做任何事,而不是特别用于某个方面。”
在 Eclipse 平台开发期间,该项目产生了两个用户界面工具箱:SWT 和 JFace。SWT 提供了一个与平台无关的 API,该 API 与操作系统的本机窗口环境紧密地集成在一起。该工具箱使开发人员不必面对在使用 Java 的抽象窗口工具箱(Abstract Window Toolkit,AWT)或 Java 基础类(Java Foundation Classes,JFC)时在许多设计和实现方面所要作的权衡。JFace 工具箱是与平台无关的 API,可与 SWT 进行互操作。该库提供了一组组件和助手实用程序,它们使开发 SWT 用户界面中的许多常见任务得以简化。
尽管在开发本文中的样本代码时使用了 Eclipse,但文章本身演示如何在独立应用程序的上下文内使用 JFace 向导。
地址簿应用程序
在查看如何使用 JFace 工具箱的代码和实现细节之前,让我们查看一下这个示例应用程序的用途和结构。本文所包含的应用程序是一个简单的内存中的地址簿,它允许用户添加个人地址簿联系人。该应用程序包含了一个由两页组成的 JFace 向导,每一页都包含了描述特定联系人的域。用户可以使用 图 1中所示的表来浏览联系人列表。
图 1. 在 Red Hat Linux 8 上运行的地址簿应用程序
如 图 2所示,New Contact 向导供用户创建新的联系人。
图 2. 用 Microsoft Windows XP 上的 New Contact 向导来说明数据验证
环境需求
要测试地址簿应用程序,您的环境必须满足下列最低需求:
Microsoft Windows XP 或 Red Hat Linux 8 操作系统
Java 2 SDK,标准版 1.4 或更高版本
Apache Ant 1.5.2 或更高版本
虽然已经很小心地使 Ant 构建脚本可以跨平台使用,但是仍然只在 Microsoft Windows XP 和 Red Hat Linux 8 操作系统上测试和验证了个人地址簿应用程序。让我们从安装和设置样本代码开始吧。
要安装和构建地址簿应用程序,请完成下列步骤:
下载源代码软件包。
将 addressbook.zip 文件解压缩到临时目录中。
在新创建的目录中运行下面这个命令: ant clean
在新创建的目录中运行下面这个命令: ant
如果您的环境满足需求并且配置正确,那么您应当会看到类似于下面这样的内容:
清单 1. 构建地址簿应用程序
2 init:
3 [mkdir] Created dir: D:\Address Book\dist
4 compile-common:
5 compile-module:
6 [echo] Compiling ...
7 [mkdir] Created dir: D:\Address Book\build
8 [mkdir] Created dir: D:\Address Book\build\classes
9 [javac] Compiling 8 source files to D:\Address Book\build\classes
10 package-common:
11 [jar] Building jar: D:\Address Book\dist\addressbook.jar
12 default:
13 BUILD SUCCESSFUL
14
使用 JFace 向导
既然我们已经介绍了 SWT 和 JFace 的基础知识以及地址簿应用程序的行为,让我们研究一下如何使用 JFace 向导。由于本文的目的在于介绍 JFace 向导,因此并没有提供有关如何创建独立应用程序的更多背景信息。
在地址簿应用程序中, NewContactAction 类处理用户从 File 菜单选择 New Contact这一操作(请参阅 图 3)。
图 3. New Contact 菜单项

清单 2中的代码说明了为用户打开一个向导所需的对象和方法。
清单 2. NewContactAction 类
2 2 {
3 3 ApplicationWindow window;
4 4
5 5 publicNewContactAction(ApplicationWindow w)
6 6 {
7 7 window = w;
8 8 this.setText("New Contact");
9 9 this.setToolTipText("Create new contact");
10 10 }
11 11
12 12 public void run()
13 13 {
14 14 ContactWizard wizard = new ContactWizard();
15 15
16 16 WizardDialog dialog = new WizardDialog(window.getShell(), wizard);
17 17 dialog.open();
18 18 }
19 19 }
20
让我们逐步研究这段代码:
无论用户何时从菜单栏选择 New Contact,都要执行第 12 行中的 run 方法。
如第 14 行所示,在向用户显示 GUI 向导之前需要创建 ContactWizard 类的新实例。该类设置向导的各个页面,允许用户向各个域提供输入。我们稍后将更加详细地介绍 ContactWizard 类。
第 16 行用两个变量对 WizardDialog 类进行实例化:第一个变量包含了对 shell 的引用,第二个变量包含了对第 14 行中所创建的 ContactWizard 的引用。 WizardDialog 类负责为用户显示实际向导以及组织每个页面的表现形式。
如 图 4所示,第 17 行打开了向导对话框。
图 4. 在 Microsoft Windows XP 上运行的 Contact 向导
要在地址簿应用程序中创建 ContactWizard 类,我们需要创建继承向导基本实现的子类并覆盖下列方法:
public void addPages() - 该方法提供了一个挂钩,以便在向最终用户显示向导之前给向导添加其它页面。要给向导添加新的页面,只要如 清单 3 所示的那样调用 addPage 方法即可。在 ContactWizard 类中定义了两个页面。我们将在下一节中研究如何创建新的页面。
清单 3. ContactWizard 类中的 addPages 方法
2 {
3 page1 = new BasicContactPage(selection);
4 page2 = new AddressContactPage(selection);
5 addPage(page1);
6 addPage(page2);
7 }
8
public boolean performFinish() - 当用户点击 Finish 按钮时执行这个方法。在 ContactWizard 中,用这个方法收集向导的每个页面上每个域中的数据并把它们填充到一个域对象中。请参阅 清单 4。
清单 4. ContactWizard 类中的 performFinish 方法
2 {
3 Contact contact = new Contact();
4 contact.setFamilyName(page1.getFamilyName());
5 contact.setGivenName(page1.getGivenName());
6 contact.setBusinessPhone(page1.getBusinessPhone());
7 contact.setHomePhone(page1.getHomePhone());
8 contact.setEmailAddress(page1.getEmail());
9
10 AddressBook.addContact(contact);
11
12 return true;
13 }
清单 5 显示了具有 addPages 和 performFinish 方法的完整类。
清单 5. ContactWizard 类
2 public class ContactWizard extends Wizard
3 {
4 private BasicContactPage page1;
5 private AddressContactPage page2;
6 private ISelection selection;
7 public ContactWizard()
8 {
9 super();
10 setNeedsProgressMonitor(true);
11 }
12 public void addPages()
13 {
14 page1 = new BasicContactPage(selection);
15 page2 = new AddressContactPage(selection);
16 addPage(page1);
17 addPage(page2);
18 }
19 public boolean performFinish()
20 {
21 Contact contact = new Contact();
22 contact.setFamilyName(page1.getFamilyName());
23 contact.setGivenName(page1.getGivenName());
24 contact.setBusinessPhone(page1.getBusinessPhone());
25 contact.setHomePhone(page1.getHomePhone());
26 contact.setEmailAddress(page1.getEmail());
27 AddressBook.addContact(contact);
28 return true;
29 }
30 }
31
没有实现 WizardPage 的类, ContactWizard 就不会有任何行为。您可以将向导看成是一堆卡片,每一张卡片都有自己的布局和设计。每个 WizardPage 负责向导中单个页面(即卡片)的布局和行为。要创建 WizardPage ,我们需要创建继承 WizardPage 基本实现的子类并实现 createControl 方法,从而为向导页面创建特定的 GUI 控件。
开发 WizardPage 时,需要完成下列各项:
使用指定父项创建一个组合。
创建窗口小部件的布局。对于 BasicContactPage 类,使用 GridLayout。
在第 2 步创建的布局中构造并布置窗口小部件。清单 6 演示了在 ContactWizard 类中构造和布置 BasicContactPage 所需的代码。请参阅 图 1中该向导页面的抓屏。
清单 6. BasicContactPage 类中的 createControl 方法
2 public void createControl(Composite parent)
3 {
4 Compositecontainer = new Composite(parent, SWT.NULL);
5 GridLayoutlayout = new GridLayout();
6 container.setLayout(layout);
7 layout.numColumns = 2;
8 layout.verticalSpacing = 9;
9
10 Label label= new Label(container, SWT.NULL);
11 label.setText("&Given Name:");
12
13 givenNameText = new Text(container, SWT.BORDER | SWT.SINGLE);
14
15 GridData gd= new GridData(GridData.FILL_HORIZONTAL);
16 givenNameText.setLayoutData(gd);
17 givenNameText.addModifyListener(new ModifyListener()
18 {
19 public void modifyText(ModifyEvent e)
20 {
21 dialogChanged();
22 }
23 });
24
25 label = newLabel(container, SWT.NULL);
26 label.setText("&Family Name:");
27
28 familyNameText = new Text(container, SWT.BORDER | SWT.SINGLE);
29 gd = newGridData(GridData.FILL_HORIZONTAL);
30 familyNameText.setLayoutData(gd);
31 familyNameText.addModifyListener(new ModifyListener()
32 {
33 public void modifyText(ModifyEvent e)
34 {
35 dialogChanged();
36 }
37 });
38
39 label = newLabel(container, SWT.NULL);
40 label.setText("&Nickname:");
41
42 nickNameText= new Text(container, SWT.BORDER | SWT.SINGLE);
43 gd = newGridData(GridData.FILL_HORIZONTAL);
44 nickNameText.setLayoutData(gd);
45
46 createLine(container, layout.numColumns);
47
48 label = newLabel(container, SWT.NULL);
49 label.setText("&Business Phone:");
50
51 businessPhoneText = new Text(container, SWT.BORDER | SWT.SINGLE);
52 gd = newGridData(GridData.FILL_HORIZONTAL);
53 businessPhoneText.setLayoutData(gd);
54
55 label = new Label(container, SWT.NULL);
56 label.setText("&Home Phone:");
57
58 homePhoneText = new Text(container, SWT.BORDER | SWT.SINGLE);
59 gd = newGridData(GridData.FILL_HORIZONTAL);
60 homePhoneText.setLayoutData(gd);
61
62 createLine(container, layout.numColumns);
63
64 label = newLabel(container, SWT.NULL);
65 label.setText("&E-Mail Address:");
66
67 emailText =new Text(container, SWT.BORDER | SWT.SINGLE);
68 gd = newGridData(GridData.FILL_HORIZONTAL);
69 emailText.setLayoutData(gd);
70 emailText.addModifyListener(newModifyListener()
71 {
72 public void modifyText(ModifyEvent e)
73 {
74 dialogChanged();
75 }
76 });
77
78 //dialogChanged();
79 setControl(container);
80 }
81 ...
82
为任何可能需要输入验证或转换的窗口小部件创建侦听器。对于 BasicContactPage 类,创建了几个 ModifyListeners 对特定数据域执行输入验证。无论何时修改文本域中的文本,都要执行 dialogChanged 方法。该方法负责处理错误并将错误报告给向导。 清单 7演示了处理输入验证和将任何错误通知向导所需的代码。
清单 7. 处理输入验证类的 dialogChanged 和 updateStatus 方法
2 private void dialogChanged()
3 {
4 if (this.getGivenName().length() == 0)
5 {
6 updateStatus("Given name must be specified.");
7 return;
8 }
9 if (this.getFamilyName().length() == 0)
10 {
11 updateStatus("Family name must be specified.");
12 return;
13 }
14 if (this.getEmail().length() > 0)
15 {
16 if (this.getEmail().indexOf("@") < 0)
17 {
18 updateStatus("Enter your email address as yourname@yourdomain.com");
19 return;
20 }
21 }
22 updateStatus(null);
23 }
24 private void updateStatus(String message)
25 {
26 setErrorMessage(message);
27 setPageComplete(message == null);
28 ...
29
运行地址簿应用程序
要测试和运行地址簿应用程序,请完成下列步骤:
在解包源代码的目录中运行下面这个命令: ant run
执行 Ant 脚本之后,应当会出现地址簿应用程序。
从 File 菜单选择 New Contact 以启动 JFace Contact 向导。请参阅 图 5。
图 5. Microsoft Windows XP 上 Contact 向导的两个页面
结束语
通过引入 SWT/JFace 用户界面工具箱,Eclipse 项目最终为 Java 开发人员提供了一种创建高度交互式客户机端应用程序的方法。使用象 JFace 向导这样的组件来研究如何用最少的代码来创建收集用户输入的灵活机制,确实令人兴奋。示例地址簿应用程序提供了一个构建和打包框架,您可以用该框架来创建自己的独立 SWT/JFace 应用程序。
下载本文中 示例的源代码