SWT 和 JFace 系列的第 2 部分擴展了我們所學的關於使用 Java? 技術、Eclipse 以及 SWT 和 JFace 庫創建簡單的 Standard Widget Toolkit (SWT) 應用程序的知識。這一期將向您展示如何使用組合框、列表、表和樹控件,以及如何使用表單佈局和可重用的 helper 方法。
編程人員可以使用 Standard Widget Toolkit (SWT) 和 JFace 庫來開發適用於 Eclipse 環境的圖形用戶界面 (GUI),以及開發單獨的 GUI 本機應用程序。
在本系列的 第 1 部分 中,我向您介紹了 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)的菜單欄相關,或者,這些菜單可以是漂浮在應用程序窗口之上的彈出式菜單。
必須將菜單定義爲以下三種互斥樣式之一:
BAR
充當 shell 的菜單欄。DROP_DOWN
從菜單欄或一個菜單項往下拉。POP_UP
從 shell 彈出,但上下文則針對於一個特定的控件。
菜單支持一些附加的可選樣式:
NO_RADIO_GROUP
不充當單選按鈕組;當菜單中包含RADIO
樣式的菜單項時可以使用它。LEFT_TO_RIGHT
或RIGHT_TO_LEFT
負責選擇文本方向。
必須將菜單項定義爲以下 5 種互斥樣式之一:
CHECK
可以是持久選定的(即複選的)。CASCADE
包含一個應該以下拉方式出現的菜單。PUSH
行爲類似於造成某一直接動作的按鈕。RADIO
行爲類似於一個CHECK
,但是隻有一個這種類型的項被選中。SEPARATOR
充當菜單項的組之間的隔離物(通常是一個條),這一項沒有任何功能。
創建一個菜單系統是相當複雜的。清單 1 顯示了一個代碼示例,該示例創建了一個可操作的菜單系統。
清單 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/tCtrl+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/tCtrl+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"); |
此代碼序列創建了以下菜單欄,該菜單欄中包含一些子菜單和一個彈出菜單(參見 圖 1、圖 2、圖 3 和 圖 4)。body
值是一個標籤控件,包含文本“Sample body”。彈出菜單與這個控件在上下文上存在關聯。
|
圖 1. 帶有 File 和 Help 菜單的菜單欄
圖 2. 下拉狀態的 File 菜單
圖 3. 下拉狀態的 Help 菜單
圖 4. 彈出菜單
正如您所見,菜單項可以具有加速器(Ctrl+?
)和記憶術(給通過 &
標識的字符加下劃線),幫助用戶使用鍵盤選擇一些項。
我使用一組 helper 方法創建了這些菜單,如清單 2 中所示。最佳實踐是創建與這些 helper 方法類似的方法,用這些方法創建重複的 GUI 部分,如菜單。隨着時間的推移,您可以向這些 helper 方法添加更多的支持功能,並將它們應用到所有使用點。這些方法還有助於提示您獲得所有需要的值。
清單 2. 菜單創建 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
),就可以處理菜單命令。
清單 3. 處理菜單命令的
Callback
例程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(); } } }); } |
我在本系列的 第 1 部分 中描述了使用 SelectionListener
的細節。
請注意,菜單項(以及稍後討論的列表、表、和樹控件中的項)只支持字符串值;在添加其他類型的值之前,這些值將被轉換成字符串值。
|
通常,您希望 GUI 的用戶從預先確定的值列表中進行選擇。列表 控件是做到這一點的最簡單的方法。列表顯示了一組預先定義的、用戶可以從中進行選擇的字符串值。列表通常需要大量的屏幕實際信息(real estate)。如果您想節省空間,那麼可以使用組合框 控件,組合框允許在需要的時候讓列表處於下拉狀態。組合框還可以有選擇地允許用戶在類似文本的字段中輸入所需要的值。
必須將組合框定義爲以下兩種互斥樣式之一:
SIMPLE
顯示值的列表。DROP_DOWN
使值的列表處於下拉狀態。
組合框支持一種可選樣式:
READ_ONLY
防止用戶編輯此組合框的文本字段。
我所討論的所有控件(列表、組合框、表和樹)都支持以下兩種互斥樣式之一:
SINGLE
用戶只能選擇一個項。MULTI
用戶可以選擇多個項。
這些控件還支持其他樣式:
H_SCROLL
在需要時顯示了一個水平滾動的條。V_SCROLL
在需要時顯示了一個垂直滾動的條。
創建組合框和列表相當容易。創建這些控件和添加所需要的字符串值,如清單 4 所示。
清單 4. 使用
FormLayout
創建一個組合框和一個列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 中所示的組合框和(部分已隱藏的)列表。
圖 5. 組合框和列表的例子
您可以使用組合框控件的一個叫做 CCombo 的替代實現(位於 org.eclipse.swt.custom
包中)。除了支持一些額外的功能,CCombo 類似於 Combo,最重要的是,您可以以編程方式要求 CCombo 將文本剪切、複製或粘貼到它的嵌入式 Text 控件中,反之亦可。此外,CCombo 總是以 DROP_DOWN
樣式出現,所以它不支持類型樣式。
CCombos 還支持一些可選樣式:
BORDER
顯示了一個圍繞文本區的邊框。READ_ONLY
防止用戶編輯該組合框的文本字段。
清單 4 中的例子使用 FormLayout
來放置組合框和列表。FormLayout
是最有用的佈局管理器之一,因爲它允許您相對於其他控件來安排每個控件,允許您將控件的任意一邊(左邊、頂部、右邊或底部)附着到另一個控件的(通常相對的)邊,或者附着到容器的某一邊上。未附着的邊則採用該控件的自然相對維數(natural corresponding dimension)。可以使用 FormAttachment
的一個實例,將引用控件或容器大小的百分比指定爲附着點,並提供距離此點的像素偏移量。清單 4 中的代碼使用了來自清單 5 的 helper 方法。
清單 5.
configureLayout
: FormLayout 幫助器方法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 中包裝的字符串數據。
表支持以下可選樣式:
CHECK
將複選框添加到第一列中。VIRTUAL
支持大型表(特定於平臺)。FULL_SELECTION
選擇所有列(不僅僅是第一列)。
清單 6 創建了圖 6 中所示的表。
清單 6. 使用 helper 方法創建一個表
// 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); } |
圖 6. 表的例子
第一列中的複選框是可選的。注意列的對齊方式。
|
樹 是可以顯示分層信息的列表。樹支持應用程序的擴展和摺疊層次結構的中間級別的能力。
因爲樹常常顯示分層結構,所以應該給它們提供一個數據模型供它們使用(在談論 JFace 時,我將再次提到這個模型概念)。爲此,在我們的例子中使用了內部類 Node
,如清單 7 所示。
清單 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 中所示的樹。
清單 8. 使用 helper 方法創建樹
// 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("<root>"); addChildren(root, 3, 3, "Child"); tree = createTree(this, SWT.CHECK, root); |
圖 7. 樹的例子
複選框是可選的。
|
除了菜單的例子之外,本文中的所有例子都使用了一個叫做 BasicApplication 的基類,以簡化它們的實現。作爲另一個最佳實踐的例子,我將 SWT GUI 應用程序的一些常見功能應用到這個基類中(包括來自菜單示例的 helper 方法),以使它們更易於使用。
BasicApplication 是一個合成物,它創建了自己的 shell。該類提供了一些額外的功能,比如退出確認對話框(參見圖 8),以及將小部件樹作爲診斷幫助工具(diagnostic aid)轉儲出來的能力(參見清單 9 中一個經過刪減的例子)。請參閱 參考資料,以獲得這個類的代碼。
圖 8. 確認消息對話框
清單 9. 控件層次結構的打印輸出(部分)
Shell {Tree1App Example} Tree1App {} Tree {} TreeItem {<root>} 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 顯示了每個子類(來自 清單 4 中組合框和列表的例子)的 main
方法,並提供了 shell 的標題和大小、應用程序合成物的樣式和所有命令行輸入。
清單 10. 示例列表應用程序的 main 方法
public static void main(String[] args) { run(List1App.class.getName(), "List1App Example", SWT.NONE, 400, 300, args); } |
每個通過 Java 反射技術加載的子類都必須定義一個構造函數和 completeGui
方法。子類可以選擇性地提供 initGui
方法。再一次使用 清單 4 中的組合框和列表應用程序作爲例子,這些方法如清單 11 中所示。
清單 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
控件請求用戶輸入選擇的信息。
必須將 MessageBox
定義爲以下 5 種互斥樣式之一:
ICON_ERROR
表示一條錯誤消息。ICON_INFORMATION
表示一條信息消息。ICON_QUESTION
表示一條問題消息。ICON_WARNING
表示一條警告消息。ICON_WORKING
表示一條運行情況消息。
MessageBoxes 支持其他一些可選樣式,所有樣式都表示了它們在按鈕上的各自選擇:
OK, OK | CANCEL
YES | NO, YES | NO | CANCEL
RETRY | CANCEL
ABORT | RETRY | IGNORE
清單 12 顯示了 MessageBox
一個典型用法,它在用戶關閉應用程序 shell 時顯示確認對話框,如 圖 8 所示。
清單 12. 使用
MessageBox
創建一個退出確認對話框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 變得更容易。
本系列的下一期將向您展示如何創建更多的容器和輸入控件,以及如何使用 StackLayout
佈局管理器。
|
描述 | 名字 | 大小 | 下載方法 |
---|---|---|---|
Java code samples | os-jface2source.ZIP | 12.2KB | HTTP |
關於下載方法的信息 |
- 如果您是新接觸 SWT 和 JFace,那麼請參閱本系列的 第 1 部分,瞭解有關創建簡單 SWT 應用程序的知識(developerWorks, 2005 年 5 月)。
- “單獨使用 JFace 和 SWT”(developerWorks, 2003 年 1 月)介紹了一些主要的 JFace 類(和少量 SWT 小部件),以及一些技巧、竅門和設計問題。
- Eclipse In Action: A Guide for Java Developers(Independent Publishers Group, 2003)是使用 Eclipse 的 Java 開發人員必須閱讀的書籍。
- 瀏覽 developerWorks 上的所有 Eclipse 文章。
- 要了解有關 Eclipse 的更多信息,請參閱 Eclipse.org。您將在那裏找到技術文檔、how-to 文章、教育、下載、產品信息和其他更多信息。
- 由 Rob Warner 和 Robert Harris 合著的 The Definitive Guide to SWT and JFace (Apress, 2004) 提供了這兩項新技術的全面概括。
- “Migrate your Swing applications to SWT”(developerWorks, 2004 年 1 月)是一個教程,其中包含 SWT 和 Swing 之間的比較和映射。
- “使用 Eclipse 平臺進行調試”(developerWorks, 2003 年 5 月)解釋瞭如何使用 Eclipse 調試器。
- 請參閱 developerWorks 的 開放源代碼專區,以獲得更多 how-to 信息、工具和項目更新,幫助您用開源技術進行開發,並將這些技術應用到 IBM 的產品中。
- 利用 IBM 試用軟件 改進您的下一個開源開發項目,這些軟件可以通過下載或從 DVD 中獲得。
- 從 Eclipse 的 Web 站點上獲得 Eclipse 的最新版本,Eclipse 是一個用於 Java 應用程序開發的開源 IDE。
- 通過參與 developerWorks blogs 加入 developerWorks 社區。
Barry Feigenbaum 博士是 IBM Worldwide Accessibility Center 的成員,該中心協助 IBM 使其產品可供殘障人士使用。Feigenbaum 博士已經發表了幾本書籍和文章,他擁有多項專利,並在諸如 JavaOne 之類的業界會議上發表演講。他曾擔任位於奧斯汀的得克薩斯大學(University of Texas)和其他學校的計算機科學系的助理教授。 |