Java GUI 开发
如果您看过 Java 的早期版本,并得出结论认为其 GUI 开发工具包尚未完全成熟,那么现在是时候再次审视它了。
Java 基础类 (JFC) 随 Java 1.2 版本引入,将 Java 推向了一个新的高度,使其可以轻松地与 Motif 和 MFC 在专业 GUI 开发领域展开正面竞争。如果您已经了解 Java 语言,那么 JFC 在编程的简易性方面可以轻松击败 Motif 和 MFC。在本文中,我将展示使用 vi 编辑器和 Java 开发工具包 (JDK) “手动”开发的代码。许多更高级的开发工具和 GUI 构建器可用于使这项工作更加轻松。
可移植性是系统设计人员的圣杯之一。1980 年的 UCSD Pascal 系统编译成可移植的 P 代码,可以在当时大多数微型计算机系统上解释执行。C 语言和基于 UNIX 操作系统 Linux 都变得流行,因为它们可以在各种平台上运行。最新的后来者是 Java。Java 程序从源代码编译成“字节码”,这是一种可移植且紧凑的机器表示形式,代表程序员编写的可执行语句。
C 和 C++ 是开发人员社区中众所周知的语言。为了帮助开发人员快速轻松地入门,Java 借鉴了 C 的大部分语法和 C++ 的相当一部分语法。所有基本语法运算符(如 +, -, *=, (), {} 等)都可以工作。对于希望理解 Java 的 OO 语法的 C 程序员来说,可以将对象视为与函数关联的 struct 结构。请注意,所有方法(函数)都是对象的一部分,因此语法
objectName.function(args);
通常使用。与 C 最显著的区别之一是缺少指针、malloc 和 free。
在我们需要访问内存中特定位置的字的时代,指针是必要的,但这也导致了大量难以阅读和维护的代码。malloc 和 free 函数为 C 提供了用于分配和释放内存的低级范例。Java 摒弃了这两者;由于 Java 程序是为 Java 虚拟机编译的,在 Java 虚拟机中,地址在编译时是未知的,因此没有指针。这具有有益的副作用,即排除了基于跳转到 BIOS 或系统磁盘格式化例程的病毒;该语言或底层虚拟机中不存在引用真实内存中特定位置的语法。
与 C++ 中一样,内存分配由运算符 new 处理,它类似于 malloc,但只能用于分配对象和数组。但是,内存的释放是自动的;没有 free 或 delete 可用,内存由运行时的一个“垃圾收集器”例程回收。听起来它让你失去了更少的控制权,但是如果你编写 C 代码,你可能不会抱怨局部变量在进入和离开函数时被分配和释放。Java 只是将这个概念扩展到数组和对象,这使得程序更可靠。
int foo() { int i; i is allocated "someplace" in memory (or register) do something with i } i is automatically reclaimed
Java 是一种面向对象的语言,它继承了 C++ 和 Smalltalk 的传统。Java 消除了一些 C++ 运算符,但有经验的 C++ 程序员可以轻松地将其技能升级到这种新语言。
Java 没有采用 C++ 的多重继承,而是提供了“接口”,接口具有规范的多重继承,但不具有实现的多重继承。这可能比 C++ 更强大,因为它允许对对象进行多重视图;也就是说,一个类可以实现多个接口,并且可以通过任何这些类型名称传递,仅公开实现该接口的对象已知的方法。这可以提供比传统 C++ 多重继承更高的类型安全性。
如果您想要一个完整的编程环境,Java 就能满足您的需求。例如,Java 程序没有使用本机 C 库,而是使用了 java.lang 中的类和方法,java.lang 是 Java 语言特性的类包。一个例子是 String——像 “Hello, world” 这样的普通带引号的字符串编译成 String 对象,String 类有诸如 compareTo、equals、substring、startsWith/endsWith 等方法。无需再担心 bcmp 与 memcmp 的区别;Java 提供了一组在任何地方都适用的方法。AWT 是本文的大部分内容的主题,它提供了一个可移植的图形用户界面层。Java.util 是一个实用程序例程包,例如随机数、集合类等。
在当今的地球村中,软件能够在任何区域设置中运行非常重要。国际化是“Java,互联网编程语言”的基础。因此,字符串和字符是 16 位 Unicode 而不是 8 位 ASCII,这并不太令人惊讶(请参阅 http://www.unicode.org/)。可能让您感到惊讶的是,Java 标识符可以用 Unicode 字符编写,这样任何语言的程序员都可以编写对其有意义的标识符(假设他们有一种输入字符的方式)。
如果需要数据库访问,Java 提供了 Java 数据库连接 (JDBC) 来访问关系数据库。它的模式松散地基于 Microsoft ODBC,但在更高的层次上运行。甚至还有到 ODBC 的桥梁,因此即使 Java 驱动程序尚不可用,您也可以访问 ODBC 数据库。(请参阅 Manu Konchady 的“使用 Java 进行数据库连接”,LJ 1998 年 11 月刊。)
为了满足快速应用程序开发的需求,JavaBeans 支持在 GUI 构建器中从可重用组件组合程序。不仅 GUI 的布局,而且整个应用程序在许多情况下都可以通过可视化地指示事件(例如按钮按下)和软件组件(例如电子表格、图形 Bean 或 HTML 查看器)之间的关系来“编写”。由于 ActiveX 市场没有像预期的那样增长,许多 ActiveX 开发人员正在将其组件转换为可移植的 JavaBeans。由于基于 Bean 及其应用程序可以在任何 UNIX、Linux 或 *BSD 系统上运行,这对 Linux 开发人员来说只能是好消息。
但是 Java 不是专有的吗?嗯,虽然 Sun 发明了 Java 和许多伴随它的技术,但它可以被称为“开放”技术。老用户会记得 Sun 如何通过使其 NFS 成为公共规范来主导 UNIX 分布式文件系统,甚至免费提供 NFS 底层的 RPC 和 XDR 层的源代码。从一开始,Java 语言的规范以及编译后的类文件的规范和格式都是公开可用的,公共 API 的源代码已包含在可免费下载的 JDK 中。编译器、运行时解释器和 API 内部部分的所有源代码都可以在非披露协议下免费获得,该协议允许免费重新分发二进制文件。没有这个,很可能就不会有适用于 Linux、SunOS4.1 或 *BSD 的 Java。事实上,有几个:JDK 端口、Kaffe 端口和其他端口。此外,许可旨在鼓励“开放 API”概念。阅读 JDK 许可条款第 2 条的摘录,每个使用 Sun 的 JDK 或任何衍生产品的 Java 开发人员都必须同意
如果被许可人创建任何与 Java 相关的 API 并将此类 API 分发给其他人用于 applet 或应用程序开发,则被许可人必须及时发布此类 API 的准确规范,供所有基于 Java 的软件开发人员免费使用。
正因为如此,自由软件社区的成员对 Java 的反应与对 Linux 的反应一样热情。有几个免费编译器、至少一个免费解释器和许多免费库可用。甚至商业公司也在免费提供一些库。探索 Java 免费软件(和付费软件)的好去处是 Gamelan(发音为 Gamma-LOHN),网址为 http://www.developer.com/。我自己的贡献包括 Jabadex(一个类似 Rolodex 的应用程序)、一组 X 颜色名称(Java 的 AWT 只有 13 种命名颜色)和其他(请参阅 http://www.darwinsys.com/freeware/)。
最近,Sun 宣布了更宽松的许可——Sun 社区源代码许可——据推测是模仿 Mozilla 许可证。它不是 BSD 版权或 GPL,但它更进一步。请参阅 http:java.sun.com/ 并查找许可。
Java 的开发人员希望 Java 的一切都是可移植的,包括如何处理 X Window 系统、MS Windows、Macintosh 和其他窗口系统。抽象窗口工具包是他们提供的解决方案。他们没有从编写在任何地方都有效的组件开始,而是编写了一个 GUI 组件库,它是三大系统提供的功能的最小公分母。它在每个平台上都使用了底层的本机组件,因此它会“看起来和感觉起来”像一个本机应用程序。这是用户接受的一个重要方面——如果用户必须学习一个全新的 GUI 才能使用您的软件,他们可能不会费心。这种方法限制了您在早期版本的 Java AWT 中可以做的事情,但对于较新版本,情况不再如此。JFC 组件使 Java 站在了功能齐全的 GUI 开发环境的最前沿。
自 1995 年首次公开发布以来,Java 的受欢迎程度一直在提高。1.0 版本包含 Applet API、基本窗口工具包 (AWT) 和众多其他 API。1.1 版本于 1996 年秋季发布,增加了大量新功能,包括国际化、GUI 控件与其操作处理代码之间更好的耦合、文本格式化和数百个新类。JDK1.2 在撰写本文时(1999 年 1 月)刚刚发布。JDK 1.2 版本也称为“Java 2”,包括 Java 基础类 (JFC)。JFC 包括 Swing GUI 类(本文的重点)、一些使残障人士更容易使用计算机的可访问性功能、2-D 图形包和原始 AWT。
2-D 图形可以被认为是 Java 的 PostScript。如果您做过任何 PostScript,您就会知道它实际上是两件事:一种脚本语言和一个标记引擎。由于 Java 已经提供了一种强大的编程语言,2-D 开发人员只需要提供“标记引擎”(在纸上标记)、变换、合成和其他花哨的图形以及一套扩展得多的字体。但是,它是以向后兼容的方式实现的:Graphics2D 对象是从 Graphics 对象子类化的。这为 Java 开发人员和用户提供了十年桌面出版发明的所有花哨的图形功能,所有这些都以平台无关的方式实现。
JFC 的 Swing Set 部分以 20 世纪 40 年代以 Duke Ellington 等伟人彻底改变流行音乐的音乐风格命名(提示:一个名为 Duke 的类似企鹅的生物是 Java 的吉祥物和标志)。Swing 有许多功能,包括
可自定义的外观和感觉
更多选择项、组合框等。
更多布局管理器、带边框的面板等。
选项卡式文件夹
表格视图小部件
树形视图小部件
工具提示
通过一次调用即可轻松创建所有标准类型的对话框
颜色、字体和其他选择器对话框
图 1 和图 2 是颜色选择器(带有工具提示)和 TreeView 程序的 UNIX 屏幕截图;这些示例以及此处显示的所有其他示例的源代码都在 FTP 站点上。
Java 1.1 的外观和感觉与底层操作系统相同。在 Motif 上,它的菜单和按钮看起来像 Motif 小部件;在 MS Windows 上,它们看起来像 Microsoft 小部件;在 Macintosh 上,像 Macintosh 小部件。它们确实是本机平台的窗口小部件。使用名为 “Peers” 的 Java 接口,1.1 程序构建并使用本机工具包组件,但开发人员永远不必考虑它。您只需使用 AWT 组件编写代码即可。
在 1.1 版本使用一段时间后,JavaSoft 的某人决定对开发人员进行民意调查。显然,50% 的人对现状感到满意,另外 30% 的人赞成与平台无关的外观和感觉,而剩下的 20% 的人希望能够提供自己的公司外观和感觉,使其在所有平台上都相同。Swing set UIFactory 就是结果。所有 Swing 组件都具有可设置的外观和感觉。外观和感觉由一个由名为 UIFactory 的类生成的组合 View-Controller 对象提供。UIFactory 功能强大——它可以动态地创建和应用新的 UI 对象,从而使程序可以按需更改其外观和感觉。我们的演示程序(见下文)有一个按钮,可让您在几种“外观和感觉”样式之间进行选择。Motif 模拟包含在 JDK 中。“Metal” 风格是 JavaSoft 开发的一种更清晰的外观。MS Windows 95 和 Macintosh UI 的专有外观和感觉的实现是可用的,但仅在这些平台上可用(出于许可而非技术原因)。
当您切换时,整个 UI 将以新的外观重新绘制,而不会丢失您到目前为止所做的任何选择。这是一个非常令人印象深刻的操作。OPEN LOOK 或 NextStep 等其他外观和感觉类也是可能的,甚至是可能的。我看到 Swing 的唯一缺点是它的性能。不要期望 Swing 应用程序像本机 C/C++ Motif/MS-Windows/Macintosh 应用程序那样流畅,尤其是在启动时间方面。但是,一旦应用程序启动,Swing 应用程序在具有足够内存的现代计算机上运行速度是可以接受的。
Java 可用于创建多种程序。最早引起广泛关注的程序之一是 Web Applet,它通过嵌入在网页中来动态扩展 Web 浏览器的行为。Java 也可用于制作 Web Servlet、后台 TCP 或 UDP 服务器或普通的基于 GUI 的桌面应用程序。后者最容易演示,因此我们将使用它们作为示例。我们在此处说的大部分内容也适用于 Applet 的 GUI 部分。
最简单的应用程序可能是一个带有退出按钮的窗口,当您运行时,它会退出。最简单的程序形式如 清单 1. 所示。
在此清单中,类 ButtonDemo1 既是“模型”(数据处理代码),又是“操作侦听器”,即响应用户事件(例如按下按钮)的代码。“类”扩展了 JFrame“,以便它可以成为顶级窗口。此外,它”实现了 ActionListener“,以便它可以提供按钮按下时调用的 actionPerformed 方法。

图 4. 演示按钮
让 GUI 布局成为自己的操作侦听器对于玩具应用程序来说效果很好。actionPerformed 方法必须以某种方式找出按下了哪个按钮。在这里并不难,但扩展性不好:随着代码变得越来越大,管理 GUI 组件之间的所有交互变得很困难。因此,最好让每个组件都有自己的操作侦听器。一种方法是使用 Java 的 “内部类”:在一个类内部编写另一个类。内部类在语法上类似于 Pascal 等语言中的嵌套过程。在清单 2 中,我只是添加了第二个按钮并重构了代码,以便每个按钮都有自己的侦听器。
清单 2. 带有内部类 ActionListener 的演示按钮
现在我们可以编写第三个版本(未显示,但在 FTP 站点上的存档文件中可用),该版本使用 CheckButtons 而不是 JButtons。此版本不会退出,但会更改列出的 GUI。
每个按钮的操作侦听器只是使用正确的完整类名称调用 UIManager 类的 setLookAndFeel 方法和实用程序类的 updateComponentTreeUI,并传入顶级窗口(JFrame 子类)。这会将树中的所有组件更改为以新选择的外观和感觉显示。由于某些组件可能需要不同的尺寸,我们再次调用 JFrame pack 例程,该例程计算所有组件的尺寸并使主窗口足够大以容纳所有组件。
这以及对其他 “操作” 组件的工作知识足以开始编写可移植的基于 Java 的 GUI 应用程序。但是,在我们能够着手开发大规模应用程序之前,我们必须考虑代码的组织和分区,理想的处理方法是使用一种称为 MVC 或模型-视图-控制器的范例。
模型-视图-控制器为将基于 GUI 的应用程序的功能分离为三个组成部分提供了一个强大的模型。简而言之,模型是跟踪数据的代码。视图是在屏幕上显示模型的代码。控制器是响应用户操作(例如鼠标单击、按钮按下等)的代码。这种分离最初于 1988 年正式用于 Smalltalk-80,现已成为面向对象开发人员构建基于 GUI 的应用程序的主要模型。这是有充分理由的:它将代码划分为三个(或更多)相当小的模块。它提供了最大的灵活性——可以有多个视图和多个控制器。例如,在幻灯片放映程序中,您可能有幻灯片放映视图和幻灯片排序器视图。两者中的任何一个或两者都将是可见的,每个都在自己的窗口中。使用 MVC,对其中任何一个的更改都将立即反映在所有视图中。因此,以结果为导向的方式看待 MVC 是一种使数据的所有视图在数据更改时动态自我更新的方式。
让我们以幻灯片放映程序为例,我称之为 JabberPoint(与 PowerPoint 完全无关)。主程序(请参阅 清单 3)只是创建模型、视图和控制器,然后将它们连接在一起。
数据或模型由一个名为 JPModel 的类维护。它只不过是一个字符串数组,只是每行都与一个样式相关联。该模型还具有某些其他数据,例如当前幻灯片编号。此外,它还具有用于更新数据的方法。此版本的程序没有任何幻灯片编辑功能(我仍然使用 vi 编辑器来编辑演示文稿的文本),但它确实在模型中具有更改当前幻灯片编号的方法。
请注意,这不是完整的源代码,而只是显示 MVC 架构所需的片段。如果您想要编译或使用的完整源代码,请访问课程作者的网站(请参阅侧边栏)并单击“免费软件”的链接。
模型包含数据和功能,并且可以由多个视图显示。它通常包含一个主程序,并且可能是 java.util.Observable 的子类。
视图是模型数据的 GUI 或显示。它通常创建一个框架,或者是一个 applet,并添加侦听器。它可能实现 java.util.Observer。
控制器处理模型和视图的事件。它通常实现侦听器接口,并通过调用模型中的方法来响应事件。
模型的一部分 Model.java 如 清单 4. 所示。
最简单的视图是 SlideShow 视图,它只是用大字母绘制当前页面。此视图是一个可以嵌入到 Frame 或 Applet 中的组件。
它如何知道其数据已更改?请注意方法 update。这不是 AWT 的 update 方法,而是 Observable 接口的一部分。此更新只是保存作为 Slide 传入的数据,并调用 AWT 的 repaint,它将调用清单中它下面几行的 paint 方法。
可以有多个视图。幻灯片放映程序通常有三个视图:幻灯片放映(我们实现)、大纲和排序器(我们尚未提供)。这些中的每一个都将是一个不同的视图,并且将注册为模型的观察者,如上所述。您可以使用 CardLayout 或某种 Tab Layout 管理器在它们之间切换,或者它们可以各自位于一个 Frame 中。由于它们使用 Observable/Observer,当您在一个窗口中更新数据时,它将立即在所有窗口中更新。
当用户执行某些操作时,将调用控制器。KeyController.java 是一个简单的控制器,它响应 PageUp 和 PageDown(或 Enter)并根据需要向上或向下移动当前页面。它与
frame.addKeyListener(new KeyController(model));
控制器不必是显式侦听器。例如,我们可以使用 MenuBar 作为侦听器,并使用语句将其连接起来
frame.setMenuBar(new MenuController(view,model));在我们的主程序中实例化 KeyController 之后。然后,它调用模型上的方法,例如 nextPage。
我们可以添加其他功能,例如 loadFile。当我们开始编写此程序的编辑部分时,我们可以向模型添加诸如 saveFile、newFile 等方法,并从此处调用它们。
一个复杂的问题是,MenuController 可能需要访问顶级框架(仅用于创建对话框的目的),但视图是框架内的组件,我们不希望 View 对其环境了解太多。解决此问题的一种方法是将框架传递到 MenuController 的构造函数中;另一种方法是让视图具有 getFrame 方法。
MVC 可能比这更复杂,尽管我们在这里介绍了基础知识。有关极其强大(和出色)的示例,请参阅 JFC/Swing 组件 JTable 和 TableDataModel。事实上,我们将在简单的 UNIX 管理工具中使用它们。
在这里,我们介绍一个简单的 Linux/UNIX 管理工具示例,该程序用于查看密码和组文件信息。用一种可移植的语言编写特定于系统的管理工具似乎很奇怪——这个工具将在大多数 UNIX 变体上工作。无论如何,Java 是一种很好的编写语言,JFC GUI 组件为更广泛的受众带来了强大工具的创建。特别是,此工具将让我们展示 JTable 小部件,该小部件提供了电子表格的大部分屏幕功能,包括动态排列的列和其他不错的选项。
由于 Java 是可移植的,因此它不提供用于读取系统密码文件的 API。我们设计并编写了一个 PW 类,该类具有与系统密码解析器返回的 C 语言结构相同的公共成员。我们还提供了一个 “PWReader” 类来读取它们,并提供了一个简单的实现,该实现仅从传统格式的密码文件读取。这不适合在大多数系统上用于生产,但可以用作简单的演示。由于这些读取器不影响 GUI,因此我们在此处不详细讨论它们,但两者的代码都在线。
由于我们希望这是一个 “好” 的应用程序,并且可能是以后通用的 UNIX 用户数据库编辑器(读取、写入、验证)的基础,因此我们将从一开始就根据模型-视图-控制器范例设计它。我将此程序称为 Ued,最初是为了纪念 1982 年左右在多伦多大学编写并由我的同事 Geoffrey Collyer 维护了一段时间的较早程序。我的程序与那个较早的 ued 没有共同的代码。UedModel 类(请参阅 UedModel.java)是程序的用户数据部分。UedView 在屏幕上显示用户或组的列表。UedControl 响应用户修改数据的请求。主要要注意的是它呈现的外观和感觉(参见图 5)。
请注意,您可以拖动列。例如,如果我们希望用户能够按用户 ID 排序,我们将让我们的排序例程询问 “表模型” 以查看当前的列顺序并将其用于排序。您可以通过单击列标题来选择列。(此功能在此处未使用,但在电子表格中使用。)或者,您可以通过在任何位置单击来选择一行中的所有字段(一个用户)。例如,这将在基于菜单的 “删除” 操作符中使用。
数据如何进入表格?JTable 的优点在于它指定了一个名为 JTableModel 的帮助类,它是您的数据模型和 JTable 之间的接口。一旦我们有了基于 PW 对象的数据模型(如上所述),JTableModel 只需要获取表格的各个字段并在请求时将它们返回给 JTable。请参阅源文件 UedTableModel.java,该文件只有大约 40 行长,其中大部分只是一个 switch 语句。同样,JFC 的对象模型使代码开发变得容易。
另请注意,主程序位于选项卡式布局中。组和属性选项卡也存在,但尚未实现,但它们确实显示了 JTabLayout 的易用性。我们只需编写
JTabbedPane mainPane = new JTabbedPane(); add tabbed pane to Frame cp.add(BorderLayout.CENTER, mainPane); add user view to tab mainPane.addTab("Users", uv); mainPane.addTab("Groups", new JLabel("Not Written Yet", JLabel.CENTER)); mainPane.addTab("Properties", new JLabel("Not Written Yet", JLabel.CENTER));
从那时起,选项卡视图的管理是自动的——当用户单击选项卡时,其内容将被置于前台并显示。
JTable/JTableModel 的妙处在于,您只需按照以下三个步骤即可轻松地使任何表格可编辑
编写一个返回 true 的例程 isEditable。
提供一个 CellEditor,它可以是 TextField 上的包装器(那么您只需要双击单元格即可开始编辑)。
为 TableModel 编写一个 setValueAt 例程,以便在用户在屏幕上更改值时调用该例程以设置程序中的值。
这就是您所需要的全部内容,尽管在实际应用程序中,您还会进行一些错误检查并设置 “需要保存” 标志。Ued 程序中的密码编辑器就是这样做的。实际上,JTable 小部件为您提供了电子表格几乎所有的用户界面部分,它只是 JFC 的 Swing Set 中包含的众多出色小部件之一。而这只是 Java 1.2 中包含的新功能之一。
Java 的近期未来显示创新速度依然迅猛。JFC 刚刚发布, साथ ही Java 1.2 版本 भी। 许多有前景的技术即将问世,包括 3-D、JTAPI、Java Sound、Java Speech 以及其他许多技术。 由于需要记住的首字母缩略词太多,请访问 JavaSoft API 页面 http://java.sun.com/products/api-overview.html。 3-D API 试图为三维图形提供全面的成像模型,并结合了 PEX、GL 和类似技术的最佳特性。 JTAPI 使 Java 程序能够控制各种规模的电话设备,从单个语音邮件调制解调器到大型专用交换机 (PBX)。 Java Media Framework 允许访问各种图像、音频和视频录制/播放,包括 Java Sound。 Java Sound 将提供多种声音格式,从简单地播放声音文件(在 1.2 版本中可用)到录制,再到完全控制合成器(如 MIDI)。 Java Speech 将包括语音合成和语音识别。
许多联系人跟踪系统可供选择,从简单的(我自己的免费软件 JabaDex)到仅限于 MS-Windows 的高级系统,例如 Symantec ACT。 当 Java Sound 和 JTAPI 发布后,联系人跟踪系统的开发人员可以编写代码来拨打电话、接听电话并整合语音邮件,甚至可以添加双向传真支持。 我们将不再需要为 Linux 编写一次,再为 MS-Windows 编写一次,再为 Macintosh 编写一次,再为 Solaris 编写一次。 正如 JavaSoft 的口号所承诺的那样,我们将能够“一次编写,到处运行”。
Ian Darwin 自 1980 年以来一直使用 UNIX 系统(最近几年主要使用 Solaris 和 OpenBSD),自 1995 年以来大量使用 Java。 他是 JabaDex(一个完全用 Java 编写的 5,000 行 Rolodex 应用程序)、两本教科书(Checking C Programs with Lint,由 O'Reilly 出版,以及 X User's Guide Volume 3: OPEN LOOK Edition,可在 CD-ROM 上获取)以及最近通过 Learning Tree International 提供的两个为期四天的 Java 编程课程的作者。 请通过 ian@darwinsys.com 给他发送电子邮件。