本文是本系列的第三篇也是最后一篇文章,在本文中,A. O. Van Emmenis 将通过添加操作、菜单栏、弹出菜单和工具栏完成在第 1 和第 2 部分中着手讨论的文件资源管理器示例。他将演示如何设置菜单项特性,如何重用菜单和工具栏中的操作,以及如何通过侦听来自查看器的事件使操作识别上下文。示例操作使用实用程序来启动程序和访问系统剪贴板。
简介
本系列的 第 1 部分着手讨论一个示例,该示例将 JFace 应用程序窗口子类化并使用树查看器和表查看器来显示文件夹和文件。在 第 2 部分中,我们做了些完善工作并使用 JFace 图像注册表添加了一些图标。
这次,我们将研究操作,您可以在菜单和工具栏中使用它们。我们将看到如何使用 Program类启动程序,及如何使用 Clipboard类访问系统剪贴板。我们已经使用了图标在查看器中显示文件和文件夹。我们将看到如何在菜单和工具栏中也使用它们。最后,我们将使操作侦听来自查看器的事件以使其对上下文敏感。
安装说明
如果要下载本文中 示例的代码,请注意我的系统设置:
请您自行完成随后所有调整名称和文件分隔符的工作,以便程序能在您的系统上正确地运行。
构建/运行指示信息
请确保以下 jar 文件位于类路径上:
C:/eclipse-2.1.0/plugins/org.eclipse.jface_2.1.0/jface.jar
C:/eclipse-2.1.0/plugins/org.eclipse.runtime_2.1.0/runtime.jar
C:/eclipse-2.1.0/plugins/org.eclipse.swt.win32_2.1.0/ws/win32/swt.jar
C:/eclipse-2.1.0/plugins/org.eclipse.ui.workbench_2.1.0/workbench.jar
C:/eclipse-2.1.0/plugins/org.eclipse.core.runtime_2.1.0/runtime.jar
为确保 Java VM 能找到您在运行时所用 GUI 的正确共享库,请使用以下参数运行 Java VM:
-Djava.library.path=C:/eclipse-2.1.0/plugins/org.eclipse.swt.win32_2.1.0/os/win32/x86/
最后,请从包含 icons 文件夹的文件夹中运行这些程序,以便示例能找到包含图标的 gif 文件。
|
继续使用第 2 部分的示例
上一篇文章结束时,我们的资源管理器应用程序如图 1 所示。
图 1. 资源管理器(V8)
我们在左边窗格中使用树查看器显示文件夹和文件。当在左边窗格中选中某个文件夹时,它所包含的文件就显示在右边窗格的表查看器中。我们对右边窗格中的各项排序以便首先出现的是文件夹。我们在两个查看器中都使用图标来表示文件和文件夹。
让我们给窗口添加一个简单的菜单栏。
|
菜单
JFace MenuManager简化了 SWT 菜单的创建和更新。菜单管理器可包含菜单项、其它菜单管理器(用于子菜单)和分隔符。一旦创建了菜单管理器,就可以用菜单栏、上下文菜单(也就是弹出菜单)或工具栏下拉菜单表示它。
同查看器一样,尽管通常不需要访问 SWT 菜单本身,但菜单管理器是助手对象而不是包装器对象。在讨论菜单之前,首先看看菜单管理器能包含什么。
操作
给菜单管理器添加操作。实际上,也可以给按钮和工具栏添加操作。其方法是:将 Action 子类化,设置希望在菜单/工具栏/按钮中出现的文本,然后实现 run()
方法以使其做您想做的事情。
让我们先看一下清单 1 中显示的示例 ExitAction:
清单 1. ExitAction(V1) |
一切都相当简单。Exit 中 x前的 &字符表明 x是该菜单项的 键盘导航键(助记符)。注:这不同于 加速键(热键)。很快就会看到这些……
|
给应用程序窗口添加菜单栏
为了配置应用程序窗口使其有菜单栏,我们在 ApplicationWindow中使用下面的方法:
protected void addMenuBar()
请记住:我们必须在创建 SWT shell 之前这么做。而且,这将调用应用程序窗口方法 createMenuManager()
,后者会返回它稍后用来创建 SWT 菜单栏的菜单管理器。我们的实现如清单 2 所示:
|
资源管理器现在如图 2 所示:
注意空菜单是被禁用的。试着用导航键 ALT+Fx关闭资源管理器应用程序。
让我们稍稍改进一下“退出”操作,如清单 3 所示:
清单 3. ExitAction(V2) |
我们添加了一个加速键(热键)、一条工具提示和一个图像(工具提示不会在菜单项上显示,但会在工具栏项上显示,稍后将看到这一点)。有一个可以直接设置加速键的方法,但在文本中 @字符后指定它更方便,因为用这种方法,加速键被添加到菜单项的文本中,如图 3 所示:
眼尖的读者或许已经注意到我们直接将一个图像描述符添加到操作中。我们真正想做的是从图像注册表获取图像描述符。问题是图像注册表只提供 Images— 无法要求它提供 ImageDescriptor。这是 Eclipse 错误数据库中的 错误 23555。
启动与文件关联的程序
运行与文件关联的程序非常有用。实际上使用 Program类来这样做也非常简单,如清单 4 所示:
清单 4. OpenAction(V1) |
我们使用 getTableSelection()
(稍后讨论该方法)从表获得选中的元素,然后检查是否恰好有一个元素被选中 — 请记住表现在是多选风格的表 — 然后获取元素本身,确定它确实是文件而不是文件夹,然后启动它。
Program 的 launch()
方法负责根据文件扩展名查找关联的程序,然后运行适当的可执行文件(将绝对文件名作为参数提供)。
在尝试这个操作之前,让我们先实现最后一个操作。
使用系统剪贴板
这里简单地利用一下系统剪贴板。 CopyFileNamesToClipboardAction
操作将所有选中文件的绝对文件名复制到剪贴板中。
我们使用 Clipboard对象将文本传送到系统剪贴板。
首先使 Util
类简单地创建一个剪贴板对象,如清单 5 所示:
|
要将文本放入剪贴板,可对 Clipboard 使用以下方法:
public void setContents(Object[] data, Transfer[] dataTypes)
数据中的每个数组槽都与一个传送对象关联,传送对象告诉剪贴板数据是什么数据类型。在本例中,我们使用 TextTransfer对象,它告诉剪贴板我们所传送的是纯文本(而不是 RTF 或别的)。
public void setContents(Object[] data, Transfer[] dataTypes)
的参数是数组,因此可以一次传送几种格式的数据。例如,某个字处理应用程序可能要传送 RTF 和纯文本格式的文本。
在清单 6 中展开的代码中,我们做了以下事情:
清单 6. CopyFileNamesToClipboardAction(V1)
|
最后,更改资源管理器,添加 getTableSelection()
方法以及给菜单栏添加两个新操作的代码,如下所示(清单 7):
|
资源管理器实例图
于是,我们有了 3 个操作和另外 8 个类。让我们查阅资源管理器的实例图(图 4)(已实现的类以深灰色显示)。
让我们运行这一版本,看看这些操作(图 5):
避免 GUI 实例层次结构的混乱
依我的经验,在设计 GUI 对象时,往往最后构建了相当庞大的实例层次结构。不同层次结构中的低层对象要相互知道对方,因此人们往往会在它们之间创建专门的交叉引用,而这会导致代码十分混乱。
即使在我们这样小的示例中,也可以看到混乱的苗头。操作对象需要知道表查看器中当前选中的项。问题是:操作存储什么对象?是表查看器或 SWT 表本身,还是窗口?
一种解决方案是让所有对窗口小部件/选择对象的访问都经过窗口。让每个操作存储创建它的窗口,然后对该窗口使用取值方法获取它所需的对象。
如果希望在不同窗口间共享操作(在不同窗口中用不同的方法名称访问窗口小部件),可以把通过操作对窗口小部件的访问包装在一个方法中,然后在该操作的子类中重新实现该方法以访存正确的窗口小部件。
例如,要共享访问“选择”所需的操作,可将超类中的方法 getSelection()
在某个子类中实现为:
window.getTableSelection()
而在另一个子类中(比方说)实现为
window.getThirdListViewerSelection()
。
使操作对上下文敏感
可以对操作做的另一件事是使它们知道窗口其它地方所发生的事,并据此自动适应之。我们将使 OpenAction
侦听表查看器中当前选择的任何变化。当它注意到变化时,会查看新的选择并更改它的文本、工具提示和启用状态来反映这一变化。
OpenAction
对当前选中文件启动“关联的”程序。如果当时什么也没有选中,与其让它运行并报错(或干脆令人费解地什么也不干),倒不如禁用该操作。
尽管我们是在为用户着想,但如果我看到菜单项被禁用却不知道原因的话,通常我会觉得困惑。所以,有这样一个办法:能不能更改工具提示以便能让用户也知道 为什么禁用该操作呢?
我们还要确定当选择包括几个文件时要怎么做。对,我们也许可以逐一地运行所有文件,但是,由于我曾有过在 Windows 文件资源管理器中选中 300 个文件后选择打开选项的痛苦经历,所以还是暂时先禁用打开操作吧。
最后,我们将检查选中的项是否为文件夹,如果是,那么也禁用打开操作。
我们要使 OpenAction
侦听来自表查看器的 SelectionChanged 事件,因此我们将使它实现 ISelectionChangedListener(清单 8)。
我们将在 selectionChanged()
中这么做:
清单 8. OpenAction(V2)
|
接下来,使打开操作侦听来自表查看器的 SelectionChanged 事件(清单 9)。
清单 9. 资源管理器(V11);省略了某些未更改的代码 |
现在让我们看看已选中文件的数目如何改变打开操作(图 8):
图 8. 资源管理器(V11),显示没有选中文件时的 Edit 菜单工具栏和弹出菜单
最后,我们将添加工具栏和弹出(上下文)菜单。值得高兴的是,其实没有太多事情要做,因为在操作中已经完成了所有的艰巨工作。工具栏和弹出菜单只需共享这些操作。
与状态行和菜单栏一样,我们配置窗口使其有一个工具栏,并且实现 createToolBarManager()
方法来创建它。
对于弹出菜单则略有不同。我们用 createContents()
方法创建它,然后直接把它添加到表窗口小部件。
我们还重构了代码,将这三个操作作为字段(而不是局部变量),这样我们就可以用三种方法来访问它们。让我们看看资源管理器的最终版本(清单 10):
清单 10. 资源管理器(V12) |
现在,让我们最后一次启动资源管理器,看看操作中的工具栏和弹出菜单(图 9 和图 10)。
|
结束语
我们已经在这三篇文章中学习了许多 JFace 的知识。我们了解了如何使用相对较少的代码让可插入 JFace 窗口、查看器和菜单框架生成漂亮的用户界面。
希望您已经掌握:
不过,当然还有更多。请查阅 参考资料以获得更多信息。
|
参考资料
Program、 Clipboard、 MenuManager、 Action、 ApplicationWindow、 TextTransfer
和 ISelectionChangedListener
。
|
关于作者
A. O. Van Emmenis 是一位独立顾问,专门从事 Java/J2EE 培训和咨询工作,他在英国的剑桥工作。Van 已经在软件行业工作了约 20 年左右。他最初与对象打交道是在 CAD 行业使用 Smalltalk 的时候,他现在在工作中主要使用 Java。他对敏捷(Agile)方法和 GUI 设计特别感兴趣。您可以通过 [email protected]与 Van 联系。 |