[转载]SWT 和 JFace,第 3 部分: 简介

SWT 和 JFace,第 3 部分: 简介


SWT 和 JFace 系列的第 3 部分扩展了我们所学的关于使用 Java™ 技术、Eclipse 以及 SWT 和 JFace 库创建应用程序的知识。这一期将向您展示如何使用表树(tabular tree)、画布、样式文本、滑块、微调控制项(spinner)、刻度和其他控件,以及如何使用一些堆栈布局。

在本系列的前两期中(请参阅 参考资料), 我介绍了 Eclipse、Eclipse Standard Widget Toolkit (SWT) 和 JFace GUI 工具包,以构造 Eclipse 和单独的富 GUI(rich GUI)。还介绍了一些基本 GUI 控件和容器类型。然后展示了如何将这些控件组合到简单的工作应用程序中。我详细介绍了如何在一个菜单系统中提供这些应用程序。最后,通过创建一个使 GUI 开发变得更容易的方法的库,我演示了如何遵循最佳实践。

在这一期中,我将继续查看 org.eclipse.swt.custom 和 org.eclipse.swt.widgets 包中的各种小部件。除非另外注明,否则我所讨论的控件都位于 widgets 包中。

TableTree

第 2 部分 中,我介绍了 Tree 和 Table 控件。SWT 支持这些控件的一个混合版本,在 custom 包中,这些控件被称为 TableTree。在 Eclipse V3.1 中,Tree 控件被增强为 TableTree 的一个功能替代,TableTree 则遭到反对。图 1 展示了一个表格式(TableTree 仿真模式)的示例 Tree。正如您可以看到的,树中的每一个项都被划分到列中。“第 2 部分”中展示了如何创建这些表和树,而创建一个表 Tree 实质上就是将这两项任务组合在一起,在这里我就不再重复它们。本文中包含的 Tree 示例将演示如何创建表 Tree。创建 TableTree 的代码与创建 Tree 的代码非常相似,因此,如果需要支持 Eclipse 的以前版本,那么可以使用 TableTree 控件。


图 1. 表树的例子
展示某一家庭的 4 名成员和其他人员的表树

本文的其余部分将展示如何使用许多新的 SWT 控件。我将在一个称为 TabFolder1App 的单个应用程序的上下文中做这一介绍。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


TabFolder(和 CTabFolder)

c.gif
关于本系列

本系列包含一些描述使用 Standard Widget Toolkit (SWT) 和 JFace 库来开发应用程序的基础文章,这些库是与基本的 Eclipse 软件开发工具包一起提供的。这一系列集中描述了如何使用 SWT 和 JFace 开发独立的应用程序。不过,您所学的大部分知识都可以应用到在 Eclipse 工作台上使用 SWT 当中。

我们将从具有有限功能的一些简单的 GUI 开始,然后朝着有用的应用程序扩展。我们将介绍大多数标准的、定制的 SWT 小部件和许多 JFace 特性。在我们的讨论中,至少包含一个使用该技术的例子。

本系列假定您熟悉 Java 编程语言和基于 Java 技术的开发,并且对 Java AWT 或 Swing GUI 工具包有一定的了解。

TabFolders 是一个创建使用有限数量空间的复杂 GUI 的简便方法。一个选项卡文件夹(tab folder)被分成一个或多个选项卡(tab),其中每个选项卡都是它本身的一个完整 GUI。一次只显示一个选项卡。在 custom 包中,CTabFolder 是 TabFolder 的增强版,它看起来更好一些,并且可以支持选项卡的关闭。

必须将 TabFolders 和 CTabFolders 定义为以下两个相互排斥的样式之一:

  1. TOP —— 将选项卡放置在顶部。
  2. BOTTOM —— 将选项卡放置在底部。

CTabFolder 支持其他一些可选样式:

  • FLAT —— 为文件夹提供一个扁平的外观。
  • BORDER —— 在控件的周围显示边界。
  • CLOSE —— 允许选项卡关闭(显示一个 Close 按钮)。

与包含一些项的 Trees 和 Tables 类似,TabFolders 也包含一些定义选项卡的 TabItems(或者 CTabItems)。TabFolders 还包含多个控件(通常是 Composites),每个控件都定义了选项卡的一个内容。TabItem.setControl 方法将该控件与相关的选项卡连接起来。

图 2 展示了一个示例 TabFolder,而图 3 展示了一个使用 CTabFolder 的类似 GUI。注意,选定的 Canvas 选项卡在 CTabFolder 上有一个 Close (X) 按钮。


图 2. 带有 4 个选项卡的 TabFolder
展示有 4 个选项卡的 Tab Folder,选定的选项卡中有一幅绘图

图 3. 带有 4 个选项卡的 CTabFolder
展示有 4 个选项卡的定制 Tab Folder,选定的选项卡中有一幅绘图

第 2 部分 中介绍的方法一致,我将使用 protected 服务方法(位于超类 BasicApplication 中)来创建控件。清单 1 详细介绍了这些方法中的第一种方法,并展示了如何创建 TabFolders;也存在用于创建 CTabFolders 的类似代码。


清单 1. 用于创建 TabFolder 和 TabItem 的方法
protected TabFolder createTabFolder(Composite parent, int style) {
return new TabFolder(parent, style);
}
protected TabItem createTabItem(TabFolder parent, int style,
String text, Image icon, Control ctl) {
TabItem ti = new TabItem(parent, style);
if (text != null) {
ti.setText(text);
}
if (icon != null) {
ti.setImage(icon);
}
if (ctl != null) {
ti.setControl(ctl);
}
return ti;
}
protected TabItem createTabItem(TabFolder parent,
String text, Image icon, Control ctl) {
return createTabItem(parent, SWT.NONE, text, icon, ctl);
}


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


Canvas

Canvas 是最基本的控件类型之一,可以用它来创建定制控件或绘图。图 2图 3 展示了使用 Canvas 来绘制由重叠的矩形和椭圆形组成的图片的一个例子。在这幅绘画中,一些图片被填充,而其他一些则没有被填充。颜色、大小和位置的分配是随意的。

清单 2 展示了用于创建 Canvas 的代码。要实际地在 Canvas 上进行绘图,必须向该 Canvas 添加一个 PaintListener。每当 Canvas 需要重新绘制其客户机区域的任何部分时,都需要调用其 paintControl 方法。有两种绘制风格:

  1. 直接绘制 —— 很简单,但内容在整个重绘期间是不稳定的。
  2. 在进行绘制之前构建一个模型,然后再根据此模型进行重新绘制 —— 比较复杂,但很稳定。这通常是首选方法。

清单 2. 用于创建 Canvas 的方法
protected Canvas createCanvas(Composite parent, int style, 
PaintListener pl) {
Canvas c = new Canvas(parent, style);
if (pl != null) {
c.addPaintListener(pl);
}
return c;
}
protected Canvas createCanvas(Composite parent, PaintListener pl) {
return createCanvas(parent, SWT.NONE, pl);
}

作为绘制风格 2 的一个例子,可以考虑一下清单 3 中定义的简单模型:


清单 3. PaintItems 的层次结构
abstract protected class PaintItem {
public Color color;
public void paint(GC gc) {
gc.setForeground(color);
gc.setBackground(color);
}
}
abstract protected class BaseRectItem extends PaintItem {
public boolean fill;
public Rectangle extent;
}
protected class ElipseItem extends BaseRectItem {
public void paint(GC gc) {
super.paint(gc);
if (fill) {
gc.fillOval(extent.x, extent.y,
extent.width, extent.height);
}
else {
gc.drawOval(extent.x, extent.y,
extent.width, extent.height);
}
}
}
protected class RectangleItem extends BaseRectItem {
public void paint(GC gc) {
super.paint(gc);
if (fill) {
gc.fillRectangle(extent.x, extent.y,
extent.width, extent.height);
}
else {
gc.drawRectangle(extent.x, extent.y,
extent.width, extent.height);
}
}
}

这些绘制项都由 清单 4 中显示的 PaintListener 绘制。paintControl 方法是随在其上进行绘制的图形上下文(org.eclipse.swt.graphics 包中的 GC)一起提供的。您可以使用 GC 绘制文本和许多形状。此代码将重用通过 Display 类可用的标准系统颜色。由 Canvas 决定是否使用某种背景色填充其区域。gcObjects 集合包含所有需要绘制的 PaintItem 实例。数组 colorIds 是一个到选定的系统颜色的映射。


清单 4. 用于创建 TabFolder 和 TabItem 的方法
… new PaintListener() {
public void paintControl(PaintEvent e) {
GC gc = e.gc;
gc.setBackground(canvas.getDisplay().
getSystemColor(colorIds[0]));
Point cext = canvas.getSize();
gc.fillRectangle(0, 0, cext.x, cext.y);
for (Iterator i = gcObjects.iterator();
i.hasNext();) {
PaintItem pi = (PaintItem)i.next();
pi.paint(gc);
}
}
}…
protected static int[] colorIds = {
SWT.COLOR_WHITE, SWT.COLOR_BLUE, SWT.COLOR_CYAN,
SWT.COLOR_GRAY, SWT.COLOR_GREEN, SWT.COLOR_MAGENTA,
SWT.COLOR_RED, SWT.COLOR_YELLOW, SWT.COLOR_BLACK
};

清单 5 中显示了一些代码,这些代码先清除绘画,然后创建由一组矩形和椭圆组成的绘画。通过 GUI 上的按钮可以激活此代码。


清单 5. 用于处理绘制事件的方法
public void doClear() {
gcObjects.clear();
canvas.redraw();
}
public void doDraw() {
gcObjects.clear();
Display display = drawButton.getDisplay();
// create a bunch of objects
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
RectangleItem ri = new RectangleItem();
ri.extent = new Rectangle(nextInt(500), nextInt(250),
nextInt(500), nextInt(250));
ri.color = display.
getSystemColor(colorIds[nextInt(colorIds.length)]);
ri.fill = i % 3 == 0;
gcObjects.add(ri);
}
else {
ElipseItem ei = new ElipseItem();
ei.extent = new Rectangle(nextInt(500), nextInt(250),
nextInt(500), nextInt(250));
ei.color = display.
getSystemColor(colorIds[nextInt(colorIds.length)]);
ei.fill = i % 5 == 0;
gcObjects.add(ei);
}
}
canvas.redraw();
}


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


Spinner、Slider、Scale 和 ProgressBar

SWT 支持几种输入离散值的方法。Scale 允许在(通常很小的)整数范围内挑选一个值。Slider 允许使用类似滚动条的方法在(可能很大的)整数范围内挑选一个值。Spinner 允许挑选(通过向前/撤退按钮)或键入一个(可能为小数的)数字。注意,Spinner 是 Eclipse V3.1 中的一个新特性。ProgressBar 类似于一个只输出的 Slider,因为可以用它来展示增量活动(进度)。

通常,这些控件允许您提供最小值、最大值和初始值。除了 ProgressBars 之外,这些控件还支持增量值和页面增量值,Sliders 还支持 thumb 宽度。图 4 展示了一个 GUI,它在控件组内包含一个 Slider、一个 Spinner 和一个 Scale,在这些控件的下方是一个 ProgressBar。紧贴在进度条上的是一个(居中的)Label,它展示了进度条的值。


图 4. 控件的例子
展示了包含 Slider、Spinner、Scale、ProgressBarCustom 和其他控件的 Progress Bar 选项卡的定制 Tab Folder

必须将所有这些控件定义为以下两种相互排斥的样式之一:

  1. HORIZONTAL —— 水平地布置控件。
  2. VERTICAL —— 垂直地布置控件。

Spinners 支持其他一些可选样式:

  • WRAP —— 从高值向低值换行排列。
  • READ_ONLY —— 不允许键入输入值。

ProgressBars 支持其他一些可选样式:

  • SMOOTH —— 更新不是在截然不同的步骤中进行的。
  • INDETERMINATE —— 没有预先确定步骤数的范围;进度条只是在时间上重复。

要创建这些控件,可以使用清单 6-9 中所示的代码。正如在 第 2 部分 中所描述的,将通过 registerCallback 方法,使用 Java 反射将 SelectionListeners 添加到这些控件中。每当控件的值发生更改时,都会调用此侦听器。


清单 6. 用于创建 Slider 的方法
protected Slider createSlider(Composite parent, int style, 
int min, int current, int max,
int inc, int pageinc, int thumb,
String callback) {
Slider s = new Slider(parent, style);
if (min >= 0) {
s.setMinimum(min);
}
if (max >= 0) {
s.setMaximum(max);
}
if (current >= 0) {
s.setSelection(current);
}
if (inc >= 1) {
s.setIncrement(inc);
}
if (pageinc >= 1) {
s.setPageIncrement(pageinc);
}
if (thumb >= 1) {
s.setThumb(thumb);
}
if (callback != null) {
registerCallback(s, this, callback);
}
return s;
}
protected Slider createVSlider(Composite parent,
int min, int current, int max,
int inc, int pageinc, int thumb,
String callback) {
return createSlider(parent, SWT.VERTICAL, min, current, max,
inc, pageinc, thumb, callback);
}
protected Slider createHSlider(Composite parent,
int min, int current, int max,
int inc, int pageinc, int thumb,
String callback) {
return createSlider(parent, SWT.HORIZONTAL, min, current, max,
inc, pageinc, thumb, callback);
}


清单 7. 用于创建 Spinner 的方法
protected Spinner createSpinner(Composite parent, int style, 
int min, int current, int max,
int inc, int pageinc, String callback) {
Spinner s = new Spinner(parent, style);
if (min >= 0) {
s.setMinimum(min);
}
if (max >= 0) {
s.setMaximum(max);
}
if (current >= 0) {
s.setSelection(current);
}
if (inc >= 1) {
s.setIncrement(inc);
}
if (pageinc >= 1) {
s.setPageIncrement(pageinc);
}
if (callback != null) {
registerCallback(s, this, callback);
}
return s;
}


清单 8. 用于创建 Scale 的方法
protected Scale createScale(Composite parent, int style, 
int min, int current, int max,
int inc, int pageinc) {
Scale s = new Scale(parent, style);
if (min >= 0) {
s.setMinimum(min);
}
if (max >= 0) {
s.setMaximum(max);
}
if (current >= 0) {
s.setSelection(current);
}
if (inc >= 1) {
s.setIncrement(inc);
}
if (pageinc >= 1) {
s.setPageIncrement(pageinc);
}
return s;
}
protected Scale createVScale(Composite parent,
int min, int current, int max,
int inc, int pageinc) {
return createScale(parent, SWT.VERTICAL, min, current, max,
inc, pageinc);
}
protected Scale createHScale(Composite parent,
int min, int current, int max,
int inc, int pageinc) {
return createScale(parent, SWT.HORIZONTAL, min, current, max,
inc, pageinc);
}


清单 9. 用于创建 ProgressBar 的方法
protected ProgressBar createProgressBar(Composite parent, int style, 
int min, int current, int max) {
ProgressBar pb = new ProgressBar(parent, style);
if (min >= 0) {
pb.setMinimum(min);
}
if (max >= 0) {
pb.setMaximum(max);
}
if (current >= 0) {
pb.setSelection(current);
}
return pb;
}
protected ProgressBar createVProgressBar(Composite parent,
int min, int current, int max) {
return createProgressBar(parent, SWT.VERTICAL, min, current, max);
}
protected ProgressBar createHProgressBar(Composite parent,
int min, int current, int max) {
return createProgressBar(parent, SWT.HORIZONTAL, min, current, max);
}

您可以查询或设置这些控件的当前值。考虑一下清单 10 中定义的线程,该线程将更新 图 4 中的标签、进度条和滑块。此线程在选中“Automatic Update”按钮(即代码中的 modeButton)时启动。


清单 10. 用于更新控件的线程
protected class BarUpdater extends Thread {
protected int delay;
protected Display display;
public BarUpdater(Display display) {
this.display = display;
}
public void run() {
try {
while (true) {
try {
if (!display.isDisposed()) {
display.syncExec(new Runnable() {
public void run() {
if (!modeButton.isDisposed() &&
!scale.isDisposed()) {
delay = modeButton.getSelection()
? scale.getSelection() : -1;
}
}
});
if (delay >= 0) {
Thread.sleep(delay);
if (!display.isDisposed()) {
display.syncExec(new Runnable() {
public void run() {
if (!bar.isDisposed()) {
int v = bar.getSelection() + 1;
if (v > bar.getMaximum()) {
v = bar.getMinimum();
}
bar.setSelection(v);
if (!slider.isDisposed()) {
slider.setSelection(v);
}
if (!valueLabel.isDisposed()) {
valueLabel.setText(
Integer.toString(v));
}
}
}
});
}
}
}
Thread.sleep(100);
}
catch (InterruptedException ie) {
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}

注意,此代码小心地进行检查,看各种控件在使用之前是否已经就绪。在异步 GUI 操作中,这很关键。还会注意到,所有 GUI 访问都是在一个 syncExec(或其同类 asyncExec)方法中进行的。每当在与创建 GUI 所在的线程不同的线程上访问 GUI 时,都需要这样做。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


StyledText

正如在 第 1 部分 中描述的那样,SWT 通过 Text 控件支持纯文本的输入和显示。对于更高级的文本表示形式,需要定义字体和颜色,因此可以使用 custom 包中的 StyledText 控件。StyledText 是可由许多 Eclipse 编辑器使用的控件。请考虑一下图 5 中所示的样式文本的示例。该文本包含不同的颜色和字体修饰,比如下划线、删除线、粗体和斜体。注意,删除线和下划线只在 Eclipse V3.1 上受到支持。


图 5. StyledText 的例子
展示 Styled Text 选项卡的定制 Tab Folder,该选项卡显示了带多种样式的文本

必须将 StyledText 定义为以下两种相互排斥的样式之一:

  1. MULTI —— 显示多个行。
  2. SINGLE —— 显示单个行。

StyledText 支持其他一些可选样式:

  • WRAP —— 从控件的右边换行。
  • READ_ONLY —— 不允许键入输入值。

清单 11 显示了用于创建 StyledText 的代码。清单 12 使用简单的类似 XML 的语言展示了它的用法,以定义具有这些属性的文本的范围。


清单 11. 用于创建 StyledText 的方法
protected StyledText createStyledText(Composite parent, int style) {
return new StyledText(parent, style);
}


清单 12. StyledText 的例子
styledText = createStyledText(body, SWT.MULTI | SWT.WRAP | 
SWT.FULL_SELECTION);
styledText.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
if (e.button == 3) {
processPopup();
}
}});
TextContent tc = new TextContent(body.getDisplay());
tc.setContent(dummyContent);
styledText.setText(tc.toPlainText());
styledText.setStyleRanges(tc.getStyleRanges());
:
protected static final String dummyContent =
"Just plain text! " +
"Now is the time for all good men " +
"to come to the aid of their country " +
"To be or not to be? " +
"That is the question " +
"That's all folks! ";

StyledText 的例子使用了一个 helper 类 TextContent,以确定具有特殊属性的文本的范围。这个类包含在示例代码中,支持对文档进行分析并获得其纯文本内容,以及查找各种范围,其中的纯文本内容具有不同属性或属性组合。它创建了表示这些范围的 SWT 类 StyleRange 的一个数组。StyleRange 有一些字段,这些字段描述了这样的范围,该范围包括前景色和背景色,以及要应用的起始偏移量、长度和属性,比如字体样式(粗体或斜体)、删除线和下划线。

StyledText 所具有的功能比这里展示的还要多。例如,在将文本输入到文档中时,文本没有任何属性,即使是在具有属性的点输入它也是如此。您可以使用 StyledText 的特性来改正这一行为。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


PopupList

有时,当您希望能够将一个选择列表显示为一个弹出式菜单而不用创建一个弹出式菜单时,可以使用 PopupList 控件做到这一点。图 6 显示了如何使用此控件将选择、剪切、复制和粘贴功能添加到 图 5 的文本编辑器中。


图 6. PopupList 的例子
显示了 4 个选择的示例 Popup List,这 4 个选择是:Select All、Cut、Copy 和 Paste

清单 13 显示了 processPopup 方法的内容。注意,安置弹出式菜单的代码与它所涉及的控件(即样式文本)有关。


清单 13. PopupList 的例子
PopupList popup = createPopupList(shell, 50, popupItems);
popup.select(popupItems[0]);
Point p = styledText.getLocation();
p = shell.getDisplay().map(styledText, null, p.x, p.y);
String choice = popup.open(new Rectangle(
p.x + 100, p.y - 100, 100, 200));
if (choice != null) {
if (popupItems[0].equals(choice)) {
styledText.selectAll();
}
else if (popupItems[1].equals(choice)) {
styledText.cut();
}
else if (popupItems[2].equals(choice)) {
styledText.copy();
}
else if (popupItems[3].equals(choice)) {
styledText.paste();
}
}
:
protected static final String[] popupItems = {
"Select All", "Cut", "Copy", "Paste"
};


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


StackLayout

在前两篇文章中,我讨论了随 SWT 一起提供的几个布局管理器,其中包括 FillLayoutGridLayoutFormLayout。custom 包提供了 StackLayout,可以用它在一次只显示一个 GUI 的 TabFolder(有些类似于没有选项卡的 TabFolder)的顶部放置多个 GUI。考虑一下 图 7图 8,它们显示了一个显示带编号标签的堆栈布局的两种状态。通过“>>”按钮可以让堆栈前进,而通过“<>”按钮 4 次后的布局。


图 7. StackLayout 的例子 1
展示初始标签的 Stack Layout 的用法的例子

图 8. StackLayout 的例子 2
展示最终标签的 Stack Layout 的用法的例子

此堆栈由清单 14 中的代码创建。


清单 14. StackLayout 的例子
StackLayout stackLayout = new StackLayout();
Composite clabels = createComposite(body, SWT.BORDER,
stackLayout);
Label[] labels = new Label[5];
:
for (int i = 0; i < labels.length; i++) {
Label xlabel = new Label(clabels, SWT.CENTER);
xlabel.setText("Stack " + i);
labels[i] = xlabel;
}
stackLayout.topControl = labels[0];
:
protected Composite createComposite(Composite parent,
int style,
Layout layout) {
Composite c = new Composite(parent, style);
if (layout != null) {
c.setLayout(layout);
}
return c;
}
protected Composite createComposite(Composite parent,
Layout layout) {
return createComposite(parent, SWT.NONE, layout);
}

清单 15 显示了通过“>>”按钮到达下一个堆栈的代码。对于“<
清单 15. 前进到下一个堆栈的代码

protected int currentLabel;
public void doNext() {
++currentLabel;
if (currentLabel >= labels.length) {
currentLabel = 0;
}
stackLayout.topControl = labels[currentLabel];
clabels.layout();
}


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


结束语

SWT 和 JFace 系列的第三期中,我介绍了更多的 SWT 控件,比如用于创建表树的 Tree;用于绘图的 Canvas;用于输入数字值的 Slider、Scale 和 Spinner;用于显示进度的 ProgressBar;用于输入具有某些属性的文本的 StyledText;以及用于简单动态菜单的 PopupList。我还展示了如何使用 StackLayout 创建时间合理的重叠 GUI。本系列的下一期将展示如何使用更多的 SWT 控件。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130705/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/374079/viewspace-130705/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值