本文共 15686 字,大约阅读时间需要 52 分钟。
SWT 和 JFace 系列的第 2 部分扩展了我们所学的关于使用 Java? 技术、Eclipse 以及 SWT 和 JFace 库创建简单的 Standard Widget Toolkit (SWT) 应用程序的知识。这一期将向您展示如何使用组合框、列表、表和树控件,以及如何使用表单布局和可重用的 helper 方法。
编程人员可以使用 Standard Widget Toolkit (SWT) 和 JFace 库来开发适用于 Eclipse 环境的图形用户界面 (GUI),以及开发单独的 GUI 本机应用程序。
在本系列的 中,我向您介绍了 Eclipse、Eclipse SWT 和 JFace GUI 工具包,以构造 Eclipse 和单独的富 GUI(rich GUI)。我还介绍了一些基本的标签、文本和按钮 GUI 控件,以及复合、组和 shell 容器类型。最后,我展示了如何将这些控件组合到一个简单的工作应用程序中。
在这一期中,您将学习如何向应用程序添加菜单,如何使用一些列表输入控件,以及如何使用更高级的表和三个容器控件。我还将通过采用一些使构建 GUI 变得更容易的服务方法来演示一些最佳实践。最后,我将向您展示如何将可重用的函数应用到基本应用程序类中。
除非特别注明,所有讨论的小部件和控件都位于 org.eclipse.swt.widgets 包中。
除了最基本的 GUI 应用程序之外,几乎所有的 GUI 应用程序都需要菜单。菜单增加了任何 GUI 的可用性。菜单是动态呈现的选择列表,它对应于可用的函数(常称为命令)或 GUI 状态。正如您所期望的,您可以使用菜单小部件创建菜单。菜单可以包含其他菜单或者menuItems(菜单项),而 menuItems 也可以包含菜单(即分层的菜单)。menuItems 表示您可以执行的命令或您所选择的 GUI 状态。菜单可以与应用程序(即 shell)的菜单栏相关,或者,这些菜单可以是漂浮在应用程序窗口之上的弹出式菜单。
必须将菜单定义为以下三种互斥样式之一:
菜单支持一些附加的可选样式:
必须将菜单项定义为以下 5 种互斥样式之一:
创建一个菜单系统是相当复杂的。清单 1 显示了一个代码示例,该示例创建了一个可操作的菜单系统。
import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; : Shell shell = ...; : Label body = ...; : // Create the menu bar system Menu main = createMenu(shell, SWT.BAR | SWT.LEFT_TO_RIGHT); shell.setMenuBar(main); MenuItem fileMenuItem = createMenuItem(main, SWT.CASCADE, "&File", null, -1, true, null); Menu fileMenu = createMenu(shell, SWT.DROP_DOWN, fileMenuItem, true); MenuItem exitMenuItem = createMenuItem(fileMenu, SWT.PUSH, "E&xit Ctrl+X", null, SWT.CTRL + 'X', true, "doExit"); MenuItem helpMenuItem = createMenuItem(main, SWT.CASCADE, "&Help", null, -1, true, null); Menu helpMenu = createMenu(shell, SWT.DROP_DOWN, helpMenuItem, true); MenuItem aboutMenuItem = createMenuItem(helpMenu, SWT.PUSH, "&About Ctrl+A", null, SWT.CTRL + 'A', true, "doAbout"); // add popup menu Menu popup = createPopupMenu(shell, body); MenuItem popupMenuItem1 = createMenuItem(popup, SWT.PUSH, "&About", null, -1, true, "doAbout"); MenuItem popupMenuItem2 = createMenuItem(popup, SWT.PUSH, "&Noop", null, -1, true, "doNothing"); |
此代码序列创建了以下菜单栏,该菜单栏中包含一些子菜单和一个弹出菜单(参见 、、 和 )。body 值是一个标签控件,包含文本“Sample body”。弹出菜单与这个控件在上下文上存在关联。
![]() |
|
正如您所见,菜单项可以具有加速器(Ctrl+?)和记忆术(给通过 & 标识的字符加下划线),帮助用户使用键盘选择一些项。
我使用一组 helper 方法创建了这些菜单,如清单 2 中所示。最佳实践是创建与这些 helper 方法类似的方法,用这些方法创建重复的 GUI 部分,如菜单。随着时间的推移,您可以向这些 helper 方法添加更多的支持功能,并将它们应用到所有使用点。这些方法还有助于提示您获得所有需要的值。
protected Menu createMenu(Menu parent, boolean enabled) { Menu m = new Menu(parent); m.setEnabled(enabled); return m; } protected Menu createMenu(MenuItem parent, boolean enabled) { Menu m = new Menu(parent); m.setEnabled(enabled); return m; } protected Menu createMenu(Shell parent, int style) { Menu m = new Menu(parent, style); return m; } protected Menu createMenu(Shell parent, int style, MenuItem container, boolean enabled) { Menu m = createMenu(parent, style); m.setEnabled(enabled); container.setMenu(m); return m; } protected Menu createPopupMenu(Shell shell) { Menu m = new Menu(shell, SWT.POP_UP); shell.setMenu(m); return m; } protected Menu createPopupMenu(Shell shell, Control owner) { Menu m = createPopupMenu(shell); owner.setMenu(m); return m; } protected MenuItem createMenuItem(Menu parent, int style, String text, Image icon, int accel, boolean enabled, String callback) { MenuItem mi = new MenuItem(parent, style); if (text != null) { mi.setText(text); } if (icon != null) { mi.setImage(icon); } if (accel != -1) { mi.setAccelerator(accel); } mi.setEnabled(enabled); if (callback != null) { registerCallback(mi, this, callback); } return mi; } |
清单 3 显示了如何使用 Java 的反射 功能,利用处理菜单项的代码来链接菜单项。此功能创建了一个易于使用的方法,在这个方法中,只需要给应用程序类添加一个 public 方法(比如 doExit、doAbout 或 doNothing),就可以处理菜单命令。
protected void registerCallback(final MenuItem mi, final Object handler, final String handlerName) { mi.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { try { Method m = handler.getClass().getMethod(handlerName, null); m.invoke(handler, null); } catch (Exception ex) { ex.printStackTrace(); } } }); } |
我在本系列的 中描述了使用 SelectionListener 的细节。
请注意,菜单项(以及稍后讨论的列表、表、和树控件中的项)只支持字符串值;在添加其他类型的值之前,这些值将被转换成字符串值。
![]() ![]() |
![]()
|
通常,您希望 GUI 的用户从预先确定的值列表中进行选择。列表 控件是做到这一点的最简单的方法。列表显示了一组预先定义的、用户可以从中进行选择的字符串值。列表通常需要大量的屏幕实际信息(real estate)。如果您想节省空间,那么可以使用组合框 控件,组合框允许在需要的时候让列表处于下拉状态。组合框还可以有选择地允许用户在类似文本的字段中输入所需要的值。
必须将组合框定义为以下两种互斥样式之一:
组合框支持一种可选样式:
我所讨论的所有控件(列表、组合框、表和树)都支持以下两种互斥样式之一:
这些控件还支持其他样式:
创建组合框和列表相当容易。创建这些控件和添加所需要的字符串值,如清单 4 所示。
import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.*; : setLayout(new FormLayout()); String[] data = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10" }; Combo c = createCombo(this, data); configureLayout(c, new FormAttachment(0, 5), new FormAttachment(0, 5), new FormAttachment(100, -5), null); List l = createList(this, data); configureLayout(l, new FormAttachment(0, 5), new FormAttachment(c, 5), new FormAttachment(100, -5), new FormAttachment(100, -5)); // Create a Combo protected Combo createCombo(Composite parent, String[] data) { Combo combo = new Combo(parent, SWT.DROP_DOWN | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); combo.addSelectionListener(new SelectionListener() { : }); setComboContents(data); return combo; } // Create a List protected List createList(Composite parent, String[] data) { List list = new List(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); list.addSelectionListener(new SelectionListener() { : }); setListContents(data); return list; } public void setComboContents(String[] data) { combo.removeAll(); for (int i = 0; i < data.length; i++) { combo.add(data[i]); } } public void setListContents(String[] data) { list.removeAll(); for (int i = 0; i < data.length; i++) { list.add(data[i]); } } |
如果添加 SelectionListener,那么它允许应用程序在用户更改所选定的项时采取行动。
清单 4 中的主代码序列的流假定 SelectionListener 包含在 this 引用的一些合成物中。它创建了如图 5 中所示的组合框和(部分已隐藏的)列表。
您可以使用组合框控件的一个叫做 CCombo 的替代实现(位于 org.eclipse.swt.custom 包中)。除了支持一些额外的功能,CCombo 类似于 Combo,最重要的是,您可以以编程方式要求 CCombo 将文本剪切、复制或粘贴到它的嵌入式 Text 控件中,反之亦可。此外,CCombo 总是以 DROP_DOWN 样式出现,所以它不支持类型样式。
CCombos 还支持一些可选样式:
中的例子使用 FormLayout 来放置组合框和列表。FormLayout 是最有用的布局管理器之一,因为它允许您相对于其他控件来安排每个控件,允许您将控件的任意一边(左边、顶部、右边或底部)附着到另一个控件的(通常相对 的)边,或者附着到容器的某一边上。未附着的边则采用该控件的自然相对维数(natural corresponding dimension)。可以使用 FormAttachment 的一个实例,将引用控件或容器大小的百分比指定为附着点,并提供距离此点的像素偏移量。 中的代码使用了来自清单 5 的 helper 方法。
protected static void configureLayout(Control c, FormAttachment left, FormAttachment top, FormAttachment right, FormAttachment bottom) { FormData fd = new FormData(); if (left != null) { fd.left = left; } if (top != null) { fd.top = top; } if (right != null) { fd.right = right; } if (bottom != null) { fd.bottom = bottom; } c.setLayoutData(fd); } |
![]() ![]() |
![]()
|
表 是支持 TableColumns 的列表的增强形式。这些列将它们的数据对齐成一种更可读的形式。它们还支持列名,并能调整列的大小。要创建表,首先要创建表控件,然后添加 TableItems 中包装的字符串数据。
表支持以下可选样式:
清单 6 创建了图 6 中所示的表。
// Create the Table and TableColumns protected Table createTable(Composite parent, int mode, Object[] contents) { table = new Table(parent, mode | SWT.SINGLE | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); table.setHeaderVisible(true); table.setLinesVisible(true); createTableColumn(table, SWT.LEFT, "Column 1", 100); createTableColumn(table, SWT.CENTER, "Column 2", 100); createTableColumn(table, SWT.RIGHT, "Column 3", 100); addTableContents(contents); return table; } protected TableColumn createTableColumn(Table table, int style, String title, int width) { TableColumn tc = new TableColumn(table, style); tc.setText(title); tc.setResizable(true); tc.setWidth(width); return tc; } protected void addTableContents(Object[] items) { for (int i = 0; i < items.length; i++) { String[] item = (String[])items[i]; TableItem ti = new TableItem(table, SWT.NONE); ti.setText(item); } } : // sample creation code protected void initGui() { Object[] items = { new String[] {"A", "a", "0"}, new String[] {"B", "b", "1"}, new String[] {"C", "c", "2"}, new String[] {"D", "d", "3"}, new String[] {"E", "e", "4"}, new String[] {"F", "f", "5"}, new String[] {"G", "g", "6"}, new String[] {"H", "h", "7"}, new String[] {"I", "i", "8"}, new String[] {"J", "j", "9"} }; table = createTable(this, SWT.CHECK, items); } |
第一列中的复选框是可选的。注意列的对齐方式。
![]() ![]() |
![]()
|
树 是可以显示分层信息的列表。树支持应用程序的扩展和折叠层次结构的中间级别的能力。
因为树常常显示分层结构,所以应该给它们提供一个数据模型供它们使用(在谈论 JFace 时,我将再次提到这个模型概念)。为此,在我们的例子中使用了内部类 Node,如清单 7 所示。
public class Node { protected java.util.List children; public java.util.List getChildren() { return children; } public void setChildren(java.util.List children) { this.children = children; } public void addChild(Node node) { children.add(node); } protected String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Node(String name) { this(name, new ArrayList()); } public Node(String name, java.util.List children) { setName(name); setChildren(children); } } |
要创建树,首先要创建树控件,然后添加 TreeItems 中包装的字符串数据。TreeItems 可以包含其他 TreeItems,这样就可以创建值的层次结构。清单 8 创建了图 7 中所示的树。
// Create the Tree protected Tree createTree(Composite parent, int mode, Node root) { tree = new Tree(parent, mode | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); tree.addSelectionListener(new SelectionListener() { : }); setTreeContents(root); return tree; } protected void setTreeContents(Node root) { tree.removeAll(); TreeItem ti = new TreeItem(tree, SWT.NONE); setTreeItemContents(ti, root); } protected void setTreeItemContents(TreeItem ti, Node root) { ti.setText(root.getName()); java.util.List children = root.getChildren(); if (children != null && children.size() > 0) { for (Iterator i = children.iterator(); i.hasNext();) { Node n = (Node)i.next(); TreeItem tix = new TreeItem(ti, SWT.NONE); setTreeItemContents(tix, n); } } } : // sample creation code protected void addChildren(Node n, int count, int depth, String prefix) { if (depth > 0) { for (int i = 0; i < count; i++) { String name = prefix + '.' + i; Node child = new Node(name); n.addChild(child); addChildren(child, count, depth - 1, name); } } } Node root = new Node(""); addChildren(root, 3, 3, "Child"); tree = createTree(this, SWT.CHECK, root); |
复选框是可选的。
![]() ![]() |
![]()
|
除了菜单的例子之外,本文中的所有例子都使用了一个叫做 BasicApplication 的基类,以简化它们的实现。作为另一个最佳实践的例子,我将 SWT GUI 应用程序的一些常见功能应用到这个基类中(包括来自菜单示例的 helper 方法),以使它们更易于使用。
BasicApplication 是一个合成物,它创建了自己的 shell。该类提供了一些额外的功能,比如退出确认对话框(参见图 8),以及将小部件树作为诊断帮助工具(diagnostic aid)转储出来的能力(参见清单 9 中一个经过删减的例子)。请参阅 ,以获得这个类的代码。
Shell {Tree1App Example} Tree1App {} Tree {} TreeItem {} TreeItem {Child.0} TreeItem {Child.0.0} TreeItem {Child.0.0.0} TreeItem {Child.0.0.1} TreeItem {Child.0.0.2} TreeItem {Child.0.1} TreeItem {Child.0.1.0} TreeItem {Child.0.1.1} TreeItem {Child.0.1.2} TreeItem {Child.0.2} TreeItem {Child.0.2.0} TreeItem {Child.0.2.1} TreeItem {Child.0.2.2} TreeItem {Child.1} : TreeItem {Child.2} : |
清单 10 显示了每个子类(来自 中组合框和列表的例子)的 main 方法,并提供了 shell 的标题和大小、应用程序合成物的样式和所有命令行输入。
public static void main(String[] args) { run(List1App.class.getName(), "List1App Example", SWT.NONE, 400, 300, args); } |
每个通过 Java 反射技术加载的子类都必须定义一个构造函数和 completeGui 方法。子类可以选择性地提供 initGui 方法。再一次使用 中的组合框和列表应用程序作为例子,这些方法如清单 11 中所示。
public List1App(Shell shell, int style) { super(shell, style); // must always supply parent and style } // Allow subclasses to complete the GUI protected void completeGui(String[] args) { // create GUI here : } // Allow subclasses to initialize the GUI protected void initGui() { // finish GUI and add dynamic contents here : } |
必须将 MessageBox 定义为以下 5 种互斥样式之一:
MessageBoxes 支持其他一些可选样式,所有样式都表示了它们在按钮上的各自选择:
清单 12 显示了 MessageBox 一个典型用法,它在用户关闭应用程序 shell 时显示确认对话框,如 所示。
shell.addShellListener(new ShellAdapter() { public void shellClosed(ShellEvent e) { MessageBox mb = new MessageBox(shell, SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL); mb.setText("Confirm Exit"); mb.setMessage("Are you sure you want to exit?"); int rc = mb.open(); e.doit = rc == SWT.OK; } }); |
![]() ![]() |
![]()
|
在 SWT 和 JFace 系列的第二期中,我介绍了更多的 SWT 控件:组合框、列表、表和树。我还展示了如何为 SWT 应用程序创建基类,以及如何使用 helper 方法使构建 GUI 变得更容易。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130698/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/374079/viewspace-130698/