可以使用標準視窗小組件工具箱(Standard Widget Toolkit,SWT)和 JFace 庫來開發用於 Eclipse 環境的圖形化使用者介面,而且還可以將它們用於開發單獨的 GUI 本機應用程式。在本文中,我將介紹一些基本的 SWT(基本 GUI 對象的名稱)類型,並展示如何綜合使用它們來建立有用的應用程式。
關於 Eclipse、SWT 和 JFace
正如 Eclipse 的 Web 網站上所提到的,Eclipse 是一種通用工具平台。它是一個開放的、可用於任何東西的可擴充 IDE,沒什麼特別之處,它為工具開發人員提供了靈活性以及對軟體技術的控制。
Eclipse 為開發人員提供了生產大量 GUI 驅動的工具和應用程式的基礎。而這項功能的基礎就是 GUI 庫 SWT 和 JFace。
SWT 是一個庫,它建立了Java 版的本地主機作業系統 GUI 控制項。它依賴於本機實現。這意味著基於 SWT 的應用程式具有以下幾個關鍵特性:
它們的外觀、行為和執行類似於“本機”應用程式。
所提供的視窗小組件(widget)反映了主機作業系統上提供的視窗小組件(組件和控制項)。
主機 GUI 庫的任何特殊行為都在 SWT GUI 中得到反映。
這些目標使得 SWT 不同於 Java 技術的 Swing,Swing 的設計目標是消除作業系統的差異。
SWT 庫反映了主機作業系統的基本視窗小組件。在許多環境下,這種方法太低級。JFace 庫有助於向 SWT 應用程式中添加大量服務。JFace 並沒有隱藏 SWT,它只是擴充了 SWT。正如您將在這一系列的後面部分中看到的,SWT 最重要的擴充之一是,將應用程式的資料模型與顯示及更改它的 GUI 隔離開來。
在開始之前,我需要介紹一些 SWT 術語:
Widget —— 基本的 SWT GUI 組件(類似於 Java AWT 中的 Component 和 Swing 中的 JComponent)。Widget 是一個抽象類別。
Control —— 擁有作業系統的對等物的視窗小組件(換句話說,在作業系統中具有同一身份)。Control 是一個抽象類別。
Composite —— 包含其他控制項的控制項(類似於 Java AWT 中的 Container 和 Swing 中的 JPanel)。
Item —— 其他控制項包含的視窗小組件(該控制項可能不是複合控制項),比如列表和表。注意,包含一些項的控制項很少包含其他控制項,反之亦然。Item 是一個抽象類別。
這些視窗小組件被安排在繼承階層中。參見圖 1、圖 2 和圖 3,瞭解它們是如何安排的。在圖 2 中,Basic1 類是來自本文的類,而其他所有類都是標準的 SWT 視窗小組件。
圖 1. SWT Widget 樹
圖 2. SWT Composite 樹
圖 3. SWT Item 列表
注意,Eclipse 具有跨平台特性(因此可以在許多操作平台上運行),本文基於 Eclipse 的 Microsoft? Windows? 版本。因此,本文包含的每個例子都應該能夠不加任何更改地在其他平台上使用。還要注意的是,本文是基於 Eclipse V3.0 的。Eclipse V3.1 中添加了少許 GUI 視窗小組件類型和特性。
基本控制項
幾乎所有 SWT GUI 都是從某些基礎部分開始建立的。所有 SWT 視窗小組件都可以在 org.eclipse.swt.widget
或 org.eclipse.swt.custom
包中找到。(一些 Eclipse 外掛程式還在其他包中提供了定製的視窗小組件。)視窗小組件包中包含一些基於作業系統控制項的控制項,而定製包中則包含一些超出作業系統控制項集之外的控制項。一些定製的軟體包控制項類似於視窗小組件包中的控制項。為了避免命名衝突,定製控制項的名稱都是以“C”開始的(例如,比較 CLabel 與 Label)。
在 SWT 中,所有控制項(除了一些進階控制項,比如 shell,將在後面進行討論)在建立的時候都必須有一個父控制項(一個複合執行個體)。在建立的時候,這些控制項被自動“添加”到父控制項中,這與必須明確添加到父控制項中的 AWT/Swing 中的控制項有所不同,自動添加產生了一種“自上而下”地構造 GUI 的方法。這樣,所有控制項都可以採用一個複合父控制項(或者一個子類)作為建構函式的參數。
大多數控制項都有一些必須在建立時設定的標記選項。因此,大多數控制項還有另外一個建構函式參數,我們通常稱之為樣式,該參數提供了設定這些選項的標記。所有這些參數值都是 static final int
,並且都是在 org.eclipse.swt
包的 SWT
類中定義的。如果不需要任何參數,則可以使用 SWT.NONE
值。
標籤
標籤可能是最簡單的控制項,標籤 被用於顯示純文字(沒有顏色、特殊字型或樣式的文本)或稱為表徵圖的小映像。標籤不接受焦點(換句話說,使用者不能通過 Tab 鍵或滑鼠移動到標籤),因此,標籤無法產生輸入事件。
清單 1 展示了如何建立一個簡單的文字標籤。
清單 1. 建立一個帶文本的標籤
import org.eclipse.swt.widget.*; : Composite parent = ...; : // create a center aligned label Label label = new Label(parent, SWT.CENTER); label.setText("This is the label text");
注意,該文本是採用不同於建構函式的單獨的方法設定的。這是所有 SWT 控制項的一個典型象徵。只有父控制項和樣式是在建構函式中設定的,其他所有屬性都是在已建立的對象上設定的。
由於平台的限制,標準標籤控制項不能同時擁有文本和表徵圖。為了支援同時擁有文本和表徵圖,可以使用 CLabel 控制項,如清單 2 中所示。
清單 2. 建立一個包含文本和映像的標籤
import com.eclipse.swt.graphics.*; import org.eclipse.swt.widget.*; import org.eclipse.swt.custom.*; : Composite parent = ...; Image image = ...; : // create a left aligned label with an icon CLabel Clabel = new CLabel(parent, SWT.LEFT); label.setText("This is the imaged label text""); label.setImage(image);
文本
在標籤顯示文本的同時,您時常還想允許使用者插入文本。文本 控制項就是用於此目的的。文本可以是單行的(一個文字欄位),也可以是多行的(一個文本地區)。文本還可以是唯讀。文字欄位中沒有描述,因此,常常通過標籤控制項處理它們,以確定它們的用途。文本控制項還可以包含一個“工具提示”,提供關於控制項用途的資訊(所有控制項都支援這一特性)。
清單 3 顯示了如何使用允許使用的有限數量的特性來建立一個簡單的文字欄位。選擇預設文本是為了便於擦除。
清單 3. 建立一個包含選定的預設文本和一個限制條件的文本
import org.eclipse.swt.widget.*; : Composite parent = ...; : // create a text field Text name = new Text(parent, SWT.SINGLE); name.setText("<none>"); name.setTextLimit(50); name.setToolTipText("Enter your name -- Last, First"); name.selectAll(); // enable fast erase
按鈕
通常,您希望使用者指出應該何時進行某項操作。最常見的做法是使用按鈕 控制項。存在以下幾種樣式的按鈕:
- ARROW —— 顯示為一個指向上、下、左、右方向的箭頭。
- CHECK —— 已標記的核取記號。
- FLAT —— 沒有凸起外觀的按鈕。
- PUSH —— 瞬時按鈕(最常見的事件來源)。
- RADIO —— 具有排他性的粘性標記(sticky mark),其他所有選項按鈕都在相同的組中。
- TOGGLE —— 一個粘性按鈕。
清單 4 建立了一個“Clear”按鈕:
清單 4. 建立一個按鈕
import org.eclipse.swt.widget.*; : Composite parent = ...; : // create a push button Button clear = new Button(parent, SWT.PUSH); clear.setText("Clea&r");
名稱中的
&
導致利用緊接著的一個字母建立一個加速器,允許通過 Ctrl+<字母> 順序的方式按下按鈕(控制項順序由主機作業系統決定)。
事件監聽器
通常,您可能想在選擇按鈕(特別是某種推式按鈕)的時候執行一些操作。您可以通過向該按鈕添加一個
SelectionListener
(在
org.eclipse.swt.events
包中)做到這一點。當按鈕狀態發生改變時(通常是按鈕被按下),就會建置事件。清單 5 在單擊 Clear 按鈕時輸出一條訊息。
清單 5. 按鈕事件處理常式
import org.eclipse.swt.events.*; : // Clear button pressed event handler clear.addSelectionListener(new SelectionListener() { public void widgetelected(selectionEvent e) { System.out.println("Clear pressed!"); } public void widgetDefaultSelected(selectionEvent e) { widgetelected(e); } });
此代碼使用了一個匿名的內部類,但您還可以使用指定的內部類或單獨的類作為監聽器。多數包含兩個或更多方法的
...Listener
類還有一個類似的
...Adapter
類,這個類提供了一些空的方法實現,並且可以減少您需要編寫的代碼數量。例如,還有一個
SelectionAdapter
類,這個類實現了
SelectionListener
。
注意,在這些方法中執行的操作必須快速完成(通常不足一秒時間),或者說 GUI 的反應將是遲鈍的。更長時間的操作(比如訪問檔案)需要單獨的線程,但那是以後某期文章的主題。
複合控制項
至此,我們已經討論了一些單獨的控制項。在多數 GUI 中,許多控制項被組合在一起以提供豐富的使用者體驗。在 SWT 中,這種組合是通過 Composite 類實現的。
複合控制項可以在任何層級上進行嵌套,並且可以混合和匹配控制項,將它們作為子控制項進行組合。這樣做可以極大地減少 GUI 開發的複雜性,並為 GUI 代碼重用(通過封裝內部 GUI)創造了機會。複合控制項可以是有邊界的,並且這些邊界很容易在視覺上產生混淆,或者它們也可以是無邊界的,無縫整合到更大的組中。
清單 6. 建立一個有邊界的複合控制項。
單 6. 建立一個有邊界的複合控制項
import org.eclipse.swt.widget.*; : Composite parent = ...; : Composite border = new Composite(parent, SWT.BORDER);
除了邊界之外,Group 複合子類還支援標題。在定義排他性按鈕集合時,組通常被用來包含單選類型的按鈕。
清單 7 建立了一個有邊界的組。
清單 7. 建立一個有邊界的組
import org.eclipse.swt.widget.*; : Composite parent = ...; : Group border = new Group(parent, SWT.SHADOW_OUT); border.setText("Group Description");
shell
shell 是一種可能沒有父複合控制項的複合控制項(架構或視窗);此外,它還有一個作為父控制項的 Display,這通常也是預設設定。shell 有很多種樣式,但最常見的樣式是
SWT.SHELL_TRIM
或
SWT.DIALOG_TRIM
。shell 可以是模態的,也可以是非模態的。模態 shell 常常用於對話方塊,防止父 GUI(如果有的話)在關閉子 shell 之前被處理。
清單 8 建立了一個架構樣式的頂級非模態 shell。
清單 8. 建立一個頂級 shell
import org.eclipse.swt.widget.*; : Shell frame = new Shell(SWT.SHELL_TRIM); :
shell 可以有子 shell。這些子 shell 是與父 shell 相關的獨立桌面視窗(也就是說,如果父 shell 關閉,那麼其所有子 shell 也將關閉)。
清單 9 建立了一個對話方塊樣式的子 shell。
清單 9. 建立一個對話方塊 shell
: Shell dialog = new Shell(frame, SWT.DIALOG_TRIM); :
參見圖 4 中具有 SWT.SHELL_TRIMSee 的 shell,以及圖 5 中具有 SWT.DIALOG_TRIM 的 shell,瞭解這些值如何影響 shell 的整潔性。
圖 4. 具有 SWT.SHELL_TRIM 的 shell
圖 5. 具有 SWT.DIALOG_TRIM 的 shell
布局管理器
複合控制項常常包含多個控制項。可以使用以下兩種方法安排這些控制項:
- 絕對位置 —— 為每個控制項設定明確的 X 和 Y 位置,並通過代碼設定一定的寬度和高度。
- 託管定位 —— 每個控制項的 X、Y、寬度和高度都是通過LayoutManager 設定的。
在多數情況下,應該選擇使用 LayoutManagers,因為很容易調整它們來適應可變大小的 GUI。SWT 也提供了一些布局管理器供您使用;在這一期的系列文章中,我們將討論兩種基本的布局管理器:FillLayout 和 GridLayout。在這兩種情況下,每當重新設定複合控制項的大小,都需要進行定位。
一些布局管理器常常是專為某一個複合控制項分配的。一些布局管理器只使用它們自身的參數就可以控制,而另一些布局管理器還需要其他參數 —— LayoutData,該參數是在它們管理的複合控制項中的每個控制項上指定的。
FillLayout
FillLayout 以行或列的形式安排控制項。每個控制項所設定的大小將與填充該複合控制項所需的寬度和高度相同,在這些控制項之間,空間是平均分配的。一種特殊情況是:在僅有一個子控制項時,該控制項的大小被設定為填充整個父複合控制項的大小。
清單 10. 使用 FillLayout 建立一列控制項
import org.eclipse.swt.widget.*; import org.eclipse.swt.layouts.*; : Composite composite = ...; FillLayout fillLayout = new FillLayout(SWT.VERTICAL); composite.setLayout(fillLayout);
GridLayout
GridLayout 提供了一個功能更強大的布局方法,該方法類似於使用 HTML 表的方法。它建立了 2-D 網格的儲存格。可以將控制項放置在一個或多個儲存格中(可以稱之為儲存格跨越)。儲存格的大小可以是相等的,或者是網格寬度或高度的某個給定可變百分比。可以將控制項添加到某一行的下一個可用列中,如果這一行中沒有更多的列,那麼該控制項將移動到下一行的第一列中。
清單 11 建立了一個複合控制項,該控制項有兩行和兩個列,其中包含兩個已標記的文字欄位。這些列可以有不同的寬度。
清單 11. 建立一個控制項表
import org.eclipse.swt.widget.*; import org.eclipse.swt.layouts.*; : Composite composite = ...; GridLayout gridLayout = new GridLayout(2, false); composite.setLayout(gridLayout); Label l1 = new Label(composite, SWT.LEFT); l1.settext("First Name: "); Text first = new Text(composite, SWT.SINGLE); Label l1 = new Label(composite, SWT.LEFT); l2.setText("Last Name: "); Text last = new Text(composite, SWT.SINGLE);
GridData
考慮一下這種情況:您需要指定每個控制項如何使用其儲存格中的剩餘空間。為了給每個儲存格提供這種精確控制,添加到 GridLayout 的託管複合控制項的控制項可以擁有 GridData 執行個體(LayoutData 的子類)。
清單 12 設定了這些文字欄位,以便採用所有可用的剩餘空間(根據前面的清單)。
清單 12. 配置一個擴充到所有可用空間的布局
first.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); last.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
構建一個運行程式
現在是時候來看一下我們已經在簡單的可執行例子 Basic1 中討論過的所有 SWT 控制項了。請參閱 參考資料,以獲得該應用程式的完整原始碼。
SWT GUI 需要一個已配置好的環境來運行。這個環境是通過一個顯示執行個體提供的,該執行個體提供了對主機作業系統顯示裝置的訪問。這個顯示執行個體允許您處理每個使用者輸入(滑鼠或鍵盤)來處理您的 GUI。
清單 13 建立了一個環境和一個 GUI,並顯示了這個 GUI。
清單 13. 建立一個 GUI 應用程式並啟動它
import org.eclipse.swt.widget.*; : Display display = new Display(); Shell shell = new Shell(display); shell.setText("Shell Title"); // *** construct Shell children here *** shell.open(); // open shell for user access // process all user input events while(!shell.isDisposed()) { // process the next event, wait when none available if(!display.readAndDispatch()) { display.sleep(); } } display.dispose(); // must always clean up
此代碼建立了一個類似於圖 6 的視窗。
圖 6. 應用程式範例
結束語
在 SWT 和 JFace 系列的第一期中,我們介紹了大多數基本 SWT 視窗小組件控制項:標籤、文本、按鈕、複合控制項和 shell。這些控制項,與顯示類(display class)相結合,允許建立全功能的 GUI。