电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

用JFace和SWT构建简单的应用程序


发布日期:2018/1/20
 

简介

开放源码 Eclipse 项目是 Java 领域中最有趣的新近开发项目之一Eclipse 把自己描述成一种通用的工具平台 — 开放的可扩展 IDE可用于任何用途且没有特殊之处它的两个主要组件是名为 SWT 的图形库和与其匹配的名为 JFace 的实用程序框架

SWT 是一个窗口构件集和图形库它集成于本机窗口系统但有独立于 OS 的 API

JFace 是用 SWT 实现的 UI 工具箱它简化了常见的 UI 编程任务JFace 在其 API 和实现方面都是独立于窗口系统的它旨在使用 SWT 而不隐藏它 演示了 EclipseJFace 和 SWT 之间的关系

Eclipse WorkbenchJFace 和 SWT

Hello World

让我们从我能想到的最简单的 JFace 程序开始逐步扩充它将其构建为最常见的Hello World程序

清单 Hello(版本

import orgeclipsejfacewindow*;

import orgeclipseswtwidgets*;

public class Hello

{

public static void main(String[] args)

{

ApplicationWindow w = new ApplicationWindow(null);

wsetBlockOnOpen(true);

wopen();

DisplaygetCurrent()dispose();

}

}

这里我们创建了一个名为 Hello 的类其中 main 方法仅仅创建了一个 ApplicationWindow然后打开它setBlockOnOpen() 使 open() 阻塞直到窗口关闭为止在窗口已关闭之后我们获取当前的 Display 并除去它这会释放在操作系统中用到的资源当您运行该程序时您会看到类似图 的窗口

Hello(版本

就是如此它甚至没有说Hello World在修正它之前让我们把话题转到 JFace 窗口

JFace 应用程序窗口

窗口是顶级窗口(换句话说由 OS 窗口管理器管理的窗口)的 JFace 类JFace 窗口实际上不是顶级窗口的 GUI 对象(SWT 已经提供了一个名为 Shell)相反JFace 窗口是助手对象它知道对应的 SWT Shell 对象并提供代码来帮助创建/编辑它以及侦听它的事件等 演示了您的代码JFace 和 SWT 之间的关系

您的代码JFace Window 和 SWT Shell 之间的关系

事实上这一模型是理解 JFace 如何工作的关键它并不真的是 SWT 之上的层而且它没有试图向您隐藏 SWT相反JFace 意识到有几种使用 SWT 的常用模式而且它提供了一些实用程序代码以帮助您更方便地对这些模式编程为了做到这一点JFace 提供可使用的对象或提供可将其子类化的类(有时它两者都提供)

尽管我们仅仅直接使用了一个 ApplicationWindow但实际上它们被设计为可以子类化也可以加入特定行为它们有现成的菜单栏工具栏供您插入特定于应用程序的内容的区域和状态栏 — 全都是可选的 用 JFace File Explorer 示例本身演示了这些区域

应用程序窗口的各个部分

让我们改进 Hello使它成为 ApplicationWindow 的子类更改的行在清单 中突出显示

清单 Hello(版本

import orgeclipsejfacewindow*;

import orgeclipseswtwidgets*;

public class Hello extends ApplicationWindow

{

public Hello()

{

super(null);

}

public static void main(String[] args)

{

Hello w = new Hello();

wsetBlockOnOpen(true);

wopen();

DisplaygetCurrent()dispose();

}

}

您编写的构造函数必须调用超类构造函数(如往常一样)让我们暂时不考虑该构造函数的参数运行该程序的结果与前一个程序没有任何不同缺省情况下程序不会为我们显示任何装饰性的东西我们的程序要创建一个带有文本Hello World的按钮这个按钮要显示在内容(Contents)区域要做到这一点我们必须实现 Control createContents(Composite parent) 方法

ApplicationWindow 将在所有其它窗口构件已经创建之后但窗口在屏幕上显示之前调用该方法参数 parent 是代表内容区域的复合窗口构件这里的想法是您创建一个复合窗口构件将其添加到 parent然后添加您的窗口构件并返回您创建的复合窗口构件 演示了实例层次结构

Application Window 的实例层次结构

我们的内容目前非常简单parent 下的单一按钮如清单 所示

清单 Hello(版本

import orgeclipsejfacewindow*;

import orgeclipseswt*;

import orgeclipseswtwidgets*;

public class Hello extends ApplicationWindow

{

public Hello()

{

super(null);

}

protected Control createContents(Composite parent)

{

Button b = new Button(parent SWTPUSH);

bsetText(Hello World);

return b;

}

public static void main(String[] args)

{

Hello w = new Hello();

wsetBlockOnOpen(true);

wopen();

DisplaygetCurrent()dispose();

}

}

结果是图

Hello(版本

这就是我们要实现的我们使用 JFace 创建的第一个Hello World程序包含单一按钮的窗口现在让我们继续讨论文件资源管理器这一话题首先我们将创建显示文件夹层次结构的树查看器使用 TreeViewer 和 ApplicationWindow 一样TreeViewer 不是真正的 SWT 窗口构件它也没有打算向您隐藏 SWT 窗口构件它使用 SWT 树窗口构件来显示各项并且还使用许多其它对象来协助它不象 ApplicationWindowJFace TreeViewer 并不旨在被子类化

这里的想法是 TreeViewer 知道要显示的树的根元素当然您必须告诉它那个对象是什么TreeViewer: void setInput(Object rootElement)

为了开始显示树查看器向根元素请求子元素并显示它们然后当用户展开其中的一个子元素时树查看器向该节点请求子元素以此类推实际上并不完全是那样TreeViewer 并不直接使用域对象 — 而是使用另一个名为 ContentProvider 的对象这个对象才使用域对象如图 所示

TreeViewerContentProvider 和域对象

当然您必须实现 ContentProvider对于 TreeViewer您的类必须实现 ITreeContentProvider 接口实现 TreeContentProvider

有六个方法需要实现实际上我们不用做全部的工作只需实现其中的三个就行因此本着即时满意(instant gratification)的精神让我们暂时只考虑那几个方法吧下面的代码演示了树查看器如何向内容提供程序请求正好位于根元素下的顶级元素

ITreeContentProvider: public Object[] getElements(Object element)

随后每当它需要特定元素的子元素时它就使用以下方法

ITreeContentProvider: public Object[] getChildren(Object element)

为了知道某个节点是否有子元素(有的话会将小加号放到它旁边)树查看器只需请求该节点的子元素然后会询问有多少子元素万一您的代码需要更快捷的方法来做到这一点则您必须实现另一个方法

public boolean hasChildren(Object element)

正如您所见内容提供程序不持有对任何域对象的引用持有对这些域对象的引用的是树查看器本身它把这些域对象作为参数传递给内容提供程序中的各个方法在我们的例子中节点是 File 对象为获取子元素我们使用 listFiles()我们必须记得要检查 listFiles() 是否返回 null然后使其变成空数组为了获取顶级元素(正好位于根元素之下)我们只需重用 getChildren() 方法

getParent() 方法被用来实现 reveal(Object element) 方法后者使树查看器滚动其 SWT 树窗口构件以便显示树中特定的节点问题是如果此刻实际上并没有显示那个节点那么应该在哪里显示它?JFace 会寻找其父元素以及父元素的父元素等等直到它达到已显示的节点然后它再次回头寻找直到目标节点已显示

hasChildren() 方法只是做了显而易见(未优化)的事情最后我们有了清单 中所示的代码

清单 FileTreeContentProvider(版本

import javaio*;

import javautil*;

import orgeclipsejfaceviewers*;

public class FileTreeContentProvider implements ITreeContentProvider

{

public Object[] getChildren(Object element)

{

Ob

实现顶级 Explorer 类

我们将采用 Hello World 程序更改其名称然后用 createContents() 方法创建 TreeViewer(而不是创建一个按钮)将其内容提供程序设置为我们的文件树内容提供程序然后将输入设置到某个文件夹在这个例子中我选择的文件夹是 C: 驱动器中的顶级文件夹

需要从 createContents() 返回 SWT 窗口构件正如前面提到的JFace Tree Viewer 不是 SWT 窗口构件因此我们不能将它返回我们需要从树查看器获取真正的窗口构件我们通过使用 getTree() 做到这一点我们的主窗口类现在看起来与下面相似

清单 Explorer(版本

import javaio*;

import orgeclipsejfaceviewers*;

import orgeclipsejfacewindow*;

import orgeclipseswt*;

import orgeclipseswtwidgets*;

public class Explorer extends ApplicationWindow

{

public Explorer()

{

super(null);

}

protected Control createContents(Composite parent)

{

TreeViewer tv = new TreeViewer(parent);

tvsetContentProvider(new FileTreeContentProvider());

tvsetInput(new File(C:\\));

return tvgetTree();

}

public static void main(String[] args)

{

Explorer w = new Explorer();

wsetBlockOnOpen(true);

wopen();

DisplaygetCurrent()dispose();

}

}

运行该程序您将看到与图 相似的结果

Explorer(版本

除了样板文件代码我们只需向 Hello World 程序添加 行代码就可做到这一点正如您可能猜想的那样程序用 File 的 toString() 方法来显示这些文件这不是我们真正想要的要改变这一点我们需要提供一个标签提供程序

实现标签提供程序

正如有一个内容提供程序对象可用来获取树节点的子元素一样当需要实际显示这些节点时树查看器有另一个助手对象标签提供程序和前面一样我们需要设置它

public void setLabelProvider(IBaseLabelProvider labelProvider)

而且需要实现下面的方法以返回要为每个元素显示的文本

public String getText(Object element)

如果我们将标签提供程序添加到树查看器图中就会得到图

显示内容提供程序和标签提供程序的树查看器

我们可以实现接口 ILabelProvider但将缺省实现 LabelProvider 子类化更容易(如果没有显式地设置标签提供程序则使用的就是这个类)我们希望利用 getText() 做的事是返回文件名最后的部分 — 相对文件名而非 toString() 缺省使用的绝对文件名 演示了代码

FileTreeLabelProvider(版本

import javaio*;

import orgeclipsejfaceviewers*;

public class FileTreeLabelProvider extends LabelProvider

{

public String getText(Object element)

{

return ((File) element)getName();

}

}

而且我们必须记得使树查看器使用这个标签提供程序如清单 所示

清单 Explorer(版本

import javaio*;

import orgeclipsejfaceviewers*;

import orgeclipsejfacewindow*;

import orgeclipseswt*;

import orgeclipseswtwidgets*;

public class Explorer extends ApplicationWindow

{

public Explorer()

{

super(null);

}

protected Control createContents(Composite parent)

{

TreeViewer tv = new TreeViewer(parent);

tvsetContentProvider(new FileTreeContentProvider());

tvsetLabelProvider(new FileTreeLabelProvider());

tvsetInput(new File(C:\\));

return tvgetTree();

}

public static void main(String[] args)

{

Explorer w = new Explorer();

wsetBlockOnOpen(true);

wopen();

DisplaygetCurrent()dispose();

}

}

这一次运行该程序时我们会获得更清楚的视觉效果如图 所示

Explorer(版本

我们现在要做的是将树查看器移到左边将一个表查看器放在右边以显示在树查看器中已选中的文件夹中的文件列表

使用表查看器

为了处理表JFace 有一个 TableViewer和 TreeViewer 一样它有输入(根对象)内容提供程序和标签提供程序它比树查看器简单因为它不需要处理树 演示了内容提供程序和标签提供程序

显示内容提供程序和标签提供程序的表查看器

设置输入对象的方法与前面相同

TableViewer: void setInput(Object rootElement)

实现文件表查看器内容提供程序

让我们考虑内容提供程序这一次根元素比树查看器根元素简单表查看器仅仅期望根对象有许多子元素因此要实现的唯一有趣方法是获取子元素的方法

public Object[] getElements(Object rootElement)

要实现的接口是 IStructuredContentProvider

根对象是一个文件夹其子元素是该文件夹包含的文件/文件夹因此我们的文件表内容提供程序类与清单 类似

清单 FileTableContentProvider(版本

import javaio*;

import orgeclipsejfaceviewers*;

public class FileTableContentProvider implements IStructuredContentProvider

{

public Object[] getElements(Object element)

{

Object[] kids = null;

kids = ((File) element)listFiles();

return kids == null ? new Object[] : kids;

}

public void dispose()

{

}

public void inputChanged(Viewer viewer Object old_object Object new_object)

{

}

}

因此我们现在有两个查看器树查看器和表查看器为了将它们相邻地安置在一起我们创建了 SWT SashForm 窗口构件该窗口构件用一个用户可以调节的边框分隔其子元素然后我们将树和表添加到框格表单(sash form)(图

包含树查看器和表查看器的框格表单

接下来要做的是使表查看器查看用户在树查看器中选中的每个文件夹要做到这一点我们必须侦听事件

侦听事件

当用户在树查看器中选中一项时树查看器发出 SelectionChangedEvent 事件我们需要侦听该事件当发出该事件时需要将表的输入设置为树查看器中当前选中的文件为了侦听来自树查看器的选择更改事件我们使用下面的方法

public void addSelectionChangedListener

(ISelectionChangedListener listener)

当用户选中/取消选中树查看器中的节点时用下面的方法调用选择更改侦听器

public void selectionChanged(SelectionChangedEvent event)

为了实现该侦听器类我们将在主资源管理器窗口中编码一个匿名类在 selectionChanged() 方法中我们将需要获得刚选中的对象并使其成为表查看器的输入将所有的工作组合在一起就得到了清单

清单 Explorer(版本

import javaio*;

import orgeclipsejfaceviewers*;

import orgeclipsejfacewindow*;

import orgeclipseswt*;

import orgeclipseswtcustom*;

import orgeclipseswtwidgets*;

public class Explorer extends ApplicationWindow

{

public Explorer()

{

super(null);

}

protected Control createContents(Composite parent)

{

SashForm sash_form = new SashForm(parent SWTHORIZONTAL | SWTNULL);

TreeViewer tv = new TreeViewer(sash_form);

tvsetContentProvider(new FileTreeContentProvider());

tvsetLabelProvider(new FileTreeLabelProvider());

tvsetInput(new File(C:\\));

final TableViewer tbv = new TableViewer(sash_form SWTBORDER);

tbvsetContentProvider(new FileTableContentProvider());

tvaddSelectionChangedListener(new ISelectionChangedListener()

{

public void selectionChanged(SelectionChangedEvent event)

{

IStructuredSelection selection =

(IStructuredSelection) eventgetSelection();

上一篇:使用WAS CE开发基于JAX-WS的RESTful服务

下一篇:Maven起步——教你开始使用Maven二(图)