Eclipse In Action - 附錄D. 介紹SWT- -
《Eclipse In Action》是一本面向Eclipse初學(xué)者的教程,對Java開發(fā)有興趣的人也可以讀一讀。
筆者參與了該書的翻譯工作,并同步發(fā)布譯稿,希望各位指正。
附錄D. 介紹SWT
翻譯:Addone Squid[天堂魷魚](addone@gmail.com)
本附錄介紹了標(biāo)準(zhǔn)窗口小部件工具包(SWT)并特別介紹了:
- SWT是什么
- SWT的體系結(jié)構(gòu)
- SWT和事件、線程
- 如何運(yùn)行SWT代碼
SWT是由IBM開發(fā)的,作為抽象窗口工具包(AWT)和Swing的替代品的一種工具包。IBM的目標(biāo)是創(chuàng)建一種GUI工具包,其觀感和行為都像是系統(tǒng)中通常的窗口小部件,并且性能上也具有相同的速度。在本附錄中,我們會(huì)觀察AWT和Swing的行為,并和IBM采取的方法相比較。然后,我們將討論如何使用SWT,指出在使用中比較重要的概念和問題。
D.1 什么是標(biāo)準(zhǔn)窗口小部件工具包?
Eclipse技術(shù)概述(http://www.eclipse.org/whitepapers/eclipse-overview.pdf)將SWT描述為一種“與本地窗口系統(tǒng)結(jié)合但使用操作系統(tǒng)無關(guān)的API的窗口小部件集及圖形庫”。在深入分析這種表述之前,讓我們來看看在Java 1.0版本中提供的第一個(gè)圖形API:抽象窗口工具包(AWT)。AWT提供了一套API來構(gòu)建如標(biāo)簽、文本框、按鈕、列表和菜單之類的圖形組件,并指派操作系統(tǒng)來提供這些組件的特定實(shí)現(xiàn)。當(dāng)你構(gòu)建一個(gè)文本框時(shí),操作系統(tǒng)構(gòu)造它自己的文本框并顯示在程序窗體中--Windows上的Java文本框看起來就像一個(gè)Windows文本框,而Macintosh上的Java文本框看起來就像一個(gè)Macintosh文本框。
AWT的問題是Sun僅僅實(shí)現(xiàn)了在Java支持的所有平臺(tái)中共有的那些窗口小部件。為了解決這個(gè)問題,Sun和Netscape合作引入了Swing,這是創(chuàng)建完全跨平臺(tái)的窗口小部件集的一次嘗試。為了達(dá)到這個(gè)目標(biāo),他們用Java來編寫所有的東西,而不是指派給操作系統(tǒng)。這種方法讓Java在界面方面變得更有用,但代價(jià)是:
- 這些控件和它們運(yùn)行其上的平臺(tái)的外觀并不相符。
- 相對于本地實(shí)現(xiàn)來說,這些控件的性能要差得多。
Sun試圖通過為每種操作系統(tǒng)都開發(fā)被稱為“可插入式觀感”的東西來解決第一個(gè)問題。 然而,盡管這種方法解決了部分問題,Sun卻無法跟上操作系統(tǒng)的更新。比如,一種Windows的觀感在Windows 95、Windows 98、Windows ME、Windows 2000和Windows XP上看起來都一樣,而本地程序則會(huì)依賴于運(yùn)行的是哪個(gè)版本,看起來會(huì)不一樣。
Sun已經(jīng)在性能問題上取得了很大進(jìn)展,但一個(gè)模擬組件終究永遠(yuǎn)也不可能和本地等價(jià)組件具有同樣好的性能。在JVM中總會(huì)存在一個(gè)翻譯過程,以將模擬組件翻譯為一個(gè)本地描繪的指令集。
在Eclipse的開發(fā)過程中,IBM研發(fā)了針對這個(gè)問題的一種新方法,這是Sun的兩種方法的某種混合。SWT是一種通過Java本地接口(JNI)來訪問本地控件的窗口小部件集。只有在特定的操作系統(tǒng)上不存在的少數(shù)幾個(gè)控件才會(huì)進(jìn)行模擬。這種方法的缺陷是,在Eclipse/SWT部署的每個(gè)平臺(tái)上,都需要有本地庫。然而好處是,程序的外觀和性能都和本地程序一樣。并且,從Eclipse的2.1版開始,在大多數(shù)桌面操作系統(tǒng)和Pocket PC上都支持SWT。
D.2 SWT體系結(jié)構(gòu)
在談了一些關(guān)于SWT是什么的問題后,讓我們用圖形來再看看它。如圖D.1所示,SWT由三個(gè)基本組件組成:一個(gè)本地庫,負(fù)責(zé)與操作系統(tǒng)通訊;一個(gè)顯示類,作為SWT與GUI平臺(tái)通訊的接口;一個(gè)Shell類,作為程序的頂層窗口,可以容納窗口小部件(Widget)(控件和組件的另一種說法)。
在我們繼續(xù)之前,讓我們來研究一下我們剛剛引入的一些術(shù)語:
- 顯示(Display)--想象顯示類的最好方式是把它當(dāng)作一位管家。它執(zhí)行所有重要的任務(wù)而使得你不必去管他們。這個(gè)類所做的最重要的工作之一是將本地平臺(tái)事件和適宜在SWT中使用的那些進(jìn)行互譯。在你開發(fā)自己的程序的時(shí)候,你通常不需要對顯示類做些什么,只需在創(chuàng)建其他所有窗口前先創(chuàng)建它。
- Shell--從根本上說,一個(gè)用戶看到的窗口是被操作系統(tǒng)的窗口管理器最終控制的。Shell被用作兩種不同類型的窗體。第一種是你的程序的頂層窗體,你的GUI的其它部分在其上構(gòu)建。在這種情況下,Shell是作為顯示類的子類來創(chuàng)建的。另一種類型是一個(gè)窗體是其他窗體的子窗體,比如對話框。這種情況下,該Shell作為其父窗體的那個(gè)Shell的子類。
- 窗口小部件(Widget)--這個(gè)術(shù)語意指控件(Control)和組件(Composite)。你會(huì)發(fā)現(xiàn),在SWT文檔的各處,這三個(gè)術(shù)語是交替使用的。簡單說來,一個(gè)窗口小部件就是可以放置在其他窗口小部件中的一個(gè)GUI對象。
- 控件(Control)--具有操作系統(tǒng)對應(yīng)物的GUI項(xiàng)。例如,一個(gè)按鈕,文本區(qū)域,或者菜單。
- 組件(Composite)--一個(gè)可以擁有子窗口小部件的窗口小部件,比如工具欄和樹。最好的例子是canvas,你可以利用它,在使用不同布局的各種子canvas的輔助下構(gòu)造復(fù)雜的用戶界面。
SWT體系結(jié)構(gòu)是設(shè)計(jì)來模擬平臺(tái)程序結(jié)構(gòu)的,所以它對于窗口小部件創(chuàng)建和資源清理有很重要的影響。
D.2.1 窗口小部件創(chuàng)建
當(dāng)你在SWT中創(chuàng)建一個(gè)控件的時(shí)候,你還需要考慮它是如何在底層操作系統(tǒng)中被創(chuàng)建的。每個(gè)控件都有一個(gè)相似的構(gòu)造函數(shù)來接受兩個(gè)參數(shù):第一個(gè)指定其父窗口小部件,第二個(gè)指定此窗口小部件的樣式。這對于許多操作系統(tǒng)的工作方式來說都是必要的。當(dāng)該SWT對象被創(chuàng)建時(shí),操作系統(tǒng)的對應(yīng)對象也被創(chuàng)建了,而它需要知道自己的父對象是哪個(gè)。對于許多窗口小部件的樣式設(shè)置來說也是一樣的。一旦它們被創(chuàng)建了,其樣式就不能修改了。(所謂樣式就是一種有關(guān)窗口小部件外觀的對操作系統(tǒng)的示意。比如,當(dāng)你創(chuàng)建了一個(gè)按鈕,樣式就定義了其類型:單選按鈕,普通按鈕,復(fù)選按鈕等等。)
D.2.2 資源清理
通常,當(dāng)你使用Swing(或AWT)的時(shí)候,你只需創(chuàng)建你的窗口小部件、圖像、字體等等而無需關(guān)注它們的清理,因?yàn)槟阒喇?dāng)垃圾回收器運(yùn)行時(shí)JVM會(huì)處理它們的。然而,當(dāng)你使用SWT時(shí),你必須多加注意你對操作系統(tǒng)GUI資源的使用,因?yàn)橹挥杏邢薜馁Y源可用。
當(dāng)你在SWT中創(chuàng)建了一個(gè)基于資源的對象(例如顏色、鼠標(biāo)指針、字體或圖像)時(shí),你必須清理它。如果你不清理那些你不再需要的東西,就會(huì)發(fā)生資源泄漏,而你會(huì)在這種狀況下結(jié)束:不管是你的還是操作系統(tǒng)中運(yùn)行著的其他程序,都將無法再創(chuàng)建任何對象。
以下代碼片段分配了一個(gè)顏色(Color)資源然后清理了它:
Color blue = new Color (display, 0, 0, 255);
blue.dispose();

|
D.3 SWT與事件
你在所有SWT程序中都會(huì)看到的最常用的代碼段如下:
while (!shell.isDisposed ()) { if (!display.readAndDispatch ()) display.sleep (); }
|
這通常被稱為
信息泵或
事件調(diào)度循環(huán)。它的工作是當(dāng)頂層窗口打開時(shí)接收來自操作系統(tǒng)的事件(比如,用戶在移動(dòng)鼠標(biāo)),把它們調(diào)度到合適的SWT窗口小部件中,然后休眠直至有其他事件需要處理。在你的程序中,你需要至少有一個(gè)這樣的東西,否則你的程序?qū)o法接收操作系統(tǒng)的任何事件,這會(huì)讓用戶不大高興。
這個(gè)方法和AWT和Swing所使用的很不一樣,后者為開發(fā)者隱藏了這種機(jī)制。這在SWT中沒有隱藏,因?yàn)槿绻憔帉慡WT代碼并作為Eclipse插件的一部分,你根本不需要一個(gè)信息泵--你默認(rèn)使用了由工作臺(tái)提供的那個(gè)。
剩余的事件處理機(jī)制和AWT和Swing所用的差不多。大量的基本事件類型以及它們各自的偵聽器(Listener)、適配器(Adapter)都在包
org.eclipse.swt.events中定義。參閱在線文檔可以獲得完整列表。以下代碼段演示了如何創(chuàng)建一個(gè)事件偵聽器以及如何將其加入到一個(gè)對象中:
Button button = new Button(display, SWT.PUSH); button.addSelectionListener(new SelectListener() { public void widgetDefaultSelected( SelectionEvent e ) {} public void widgetSelected( SelectedEvent e ) { System.out.println("Button Pressed"); } });
|
不熟悉事件處理的人不必?fù)?dān)心,這很簡單。如前所述,一個(gè)事件關(guān)聯(lián)著一個(gè)諸如用戶移動(dòng)鼠標(biāo)或者窗體被最大化之類的動(dòng)作。對于每種可以接收的事件的類型,都有一個(gè)稱為
偵聽器的接口。一個(gè)偵聽器就是一個(gè)類,它知道如何處理特定事件并且還會(huì)做些有用的事情。要想創(chuàng)建一個(gè)偵聽器類,你需要?jiǎng)?chuàng)建一個(gè)類來實(shí)現(xiàn)那個(gè)與你要處理的事件相符的特定的偵聽器接口。
觀察前面的代碼段可知,我們關(guān)注的是
SelectionEvent,它在點(diǎn)擊按鈕時(shí)發(fā)出。如果你在Javadoc中查看該事件,你會(huì)看到正確的偵聽器是
SelectionListener。為了將一個(gè)偵聽器添加到一個(gè)窗口小部件類,你調(diào)用它的一個(gè)
addXXXListener()方法,正如前面代碼中用到的
addSelectionListener()。
提示 與其花時(shí)間重復(fù)Javadoc中所說的東西,我們建議你查看在線幫助。在那里你可以找到一份關(guān)于可用事件類型的完整清單,并且還包括了它們以及處理(或生成)那些事件的窗口小部件的描述。要查看該幫助,選擇[幫助]->[幫助內(nèi)容]->[平臺(tái)插件開發(fā)者指南]->[程序員指南]->[標(biāo)準(zhǔn)窗口小部件工具箱]->[窗口小部件]->[事件]。
|
D.4 SWT與線程
當(dāng)你使用SWT創(chuàng)建一個(gè)SWT程序時(shí),要考慮的一個(gè)重要因素就是所有的窗口小部件是如何與線程交互的。如果你熟悉Swing和AWT編程,那么以下內(nèi)容會(huì)比較熟悉,不過某些重要的差異還是需要注意的。
一個(gè)被稱為用戶界面線程的單一的重要線程負(fù)責(zé)處理事件,調(diào)度它們到合適的窗口小部件,以及進(jìn)行窗體描繪。沒有了它,你的程序?qū)o法做任何事情。你可能認(rèn)為我們過去說過一些這方面的內(nèi)容,我們的確曾經(jīng)說過。
在AWT和Swing中,用戶界面線程或事件處理線程對開發(fā)者是隱藏的。而在SWT中,創(chuàng)建消息泵的線程就成了用戶界面線程。這個(gè)設(shè)計(jì)決定使得要將SWT插件插入Eclipse中變得可能。與Sun的方法背離的另一點(diǎn)是,SWT的設(shè)計(jì)允許擁有多于一個(gè)的事件調(diào)度線程。(這個(gè)功能極少用到,我們提到它只是為了完整起見。)
主線程就是用戶界面線程,所以你不該執(zhí)行任何復(fù)雜的或耗時(shí)的任務(wù)(如數(shù)據(jù)庫訪問)或者其他會(huì)阻塞線程的任務(wù)。相反的,你應(yīng)該轉(zhuǎn)到其他線程去執(zhí)行那些操作。不這么做的話,將會(huì)嚴(yán)重影響你的用戶界面的響應(yīng)能力并且會(huì)給用戶帶來不便,這永遠(yuǎn)不是件好事。與此相關(guān)的事實(shí)就是,唯一允許調(diào)用SWT窗口小部件而不會(huì)引發(fā)SWTException異常的線程就是用戶界面線程。
你也許想知道在你轉(zhuǎn)到的線程完成之后如何更新用戶界面。要做到這樣,你要使用兩個(gè)輔助方法,它們是顯示類(Display)的一部分:asyncExec()和syncExec()。(Swing用戶注意:這些方法和Swing工具包類中的invokeLater()和invokeAndWait()方法同義。并且,是的,如果你覺得Sun的方法的命名更為清晰,我們贊同。)這些方法按以下方式工作:
- asyncExec(Runnable)--當(dāng)你需要更新用戶界面但并不關(guān)心具體何時(shí)更新時(shí)使用。記住,使用此方法意味著后臺(tái)線程和用戶界面間的處理不存在任何可保證的關(guān)聯(lián)。
- syncExec(Runnable)--當(dāng)你的背景線程需要先進(jìn)行用戶界面更新才能繼續(xù)處理時(shí)使用。注意,在用戶界面更新進(jìn)行之前,你的背景線程會(huì)被阻塞。
這些方法都采取了實(shí)現(xiàn)Runnable接口的類。以下代碼顯示了你一般會(huì)如何使用這些方法:
Display.getDefault().asyncExec(new Runnable() { public void run() { button.setText(new Date().toString()); } });
|
asyncExec()方法是
顯示類的一部分,所以你首先需要取得顯示類的當(dāng)前實(shí)例,這樣做就避免了在你的整個(gè)程序中傳遞
顯示類的引用。你將一個(gè)實(shí)現(xiàn)了
Runnable接口的類傳遞到
asyncExec()方法。通常你創(chuàng)建一個(gè)匿名類來進(jìn)行更新,正如前面的例子所示。
D.5 構(gòu)建和運(yùn)行SWT程序
你可能想要開始編程了,或者至少也要看一些正確的SWT代碼--很快我們就要這么做。然而,在那之前,讓我們來告訴你如何構(gòu)建和運(yùn)行代碼。
這本書是有關(guān)Eclipse的使用的,所以我們首先關(guān)注配置Eclipse以使你能進(jìn)行SWT開發(fā)。然后我們來看看需要什么來運(yùn)行代碼。最后,我們會(huì)解釋從命令行運(yùn)行你的SWT程序時(shí)的步驟。
要配置Eclipse,請按如下步驟進(jìn)行:
- 在[工作空間]視圖中選擇你的項(xiàng)目,選擇右鍵菜單中的[屬性]。
- 選擇[Java構(gòu)建路徑],然后點(diǎn)擊[[庫]]標(biāo)簽頁。
- 選擇[添加外部Jar]。注意,如果你很可能要經(jīng)常使用SWT,你可能會(huì)想要?jiǎng)?chuàng)建一個(gè)變量。
- 定位到適合你的平臺(tái)的swt.jar文件,如圖D.2所示(<eclipse-root>就是Eclipse所在的目錄):
- Linux GTK --<eclipse-root>/plugins/org.eclipse.swt.gtk_2.1.0/os/linux/x86
- Linux Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/linux/x86
- Solaris Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/solaris/sparc
- AIX Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/aix/ppc
- HPUX Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/hpux/PA_RISC
- Photon QNX --<eclipse-root>/plugins/org.eclipse.swt.photon_2.1.0/os/qnx/x86
- Mac OSX --<eclipse-root>/plugins/org.eclipse.swt.carbon_2.1.0/os/macosx/ppc
- 點(diǎn)擊[確定]。
注意 對于某些平臺(tái),比如GTK,需要不只一個(gè)的JAR來運(yùn)行SWT(GTK使用文件swt.jar和swt-pi.jar)。在這種情況下,你必須添加所有需要的JAR到classpath中。要這樣做,請對每個(gè)JAR文件重復(fù)以上步驟。所有的JAR文件都定義在同一個(gè)目錄(文件夾)中。
|
要運(yùn)行你的代碼,你需要按以下步驟進(jìn)行:
- 在[工作空間]視圖中,選擇你要運(yùn)行的包含有main的那個(gè)類。
- 選擇[運(yùn)行]->[運(yùn)行...]。
- 在[運(yùn)行]對話框中,選擇[Java Application]并點(diǎn)擊[新建]。
- 選擇[自變量]標(biāo)簽頁,并在[VM自變量]文本框中點(diǎn)擊鼠標(biāo)。
- 輸入-Djava.library.path=<path>,其中<path>依據(jù)你的操作系統(tǒng)而定,是如下之一(如圖D.3所示):
- Win32 --<eclipse-root>\plugins\org.eclipse.swt.win32_2.1.0\os\win32\x86
- Linux GTK --<eclipse-root>/plugins/org.eclipse.swt.gtk_2.1.0/os/linux/x86
- Linux Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/linux/x86
- Solaris Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/solaris/sparc
- AIX Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/aix/ppc
- HPUX Motif --<eclipse-root>/plugins/org.eclipse.swt.motif_2.1.0/os/hpux/PA_RISC
- Photon QNX --<eclipse-root>/plugins/org.eclipse.swt.photon_2.1.0/os/qnx/x86
- Mac OSX --<eclipse-root>/plugins/org.eclipse.swt.carbon_2.1.0/os/macosx/ppc
- 點(diǎn)擊[應(yīng)用]然后點(diǎn)擊[調(diào)試]。
你的程序現(xiàn)在運(yùn)行了。記得中止示例。要這么做,你可以點(diǎn)擊[控制臺(tái)]視圖的小方塊。
從命令行運(yùn)行你的代碼也差不多:
- 確保適合你的平臺(tái)的JAR文件在classpath中。
- 調(diào)用java并帶上-Djava.library.path參數(shù)(如前面的步驟所述)以及你的程序名。
D.6 使用SWT
我們已經(jīng)講述了SWT的概念以及如何設(shè)置Eclipse以構(gòu)建及運(yùn)行示例。現(xiàn)在是看看簡單的SWT示例的時(shí)候了。
我們會(huì)帶領(lǐng)你一起瀏覽組成示例的各個(gè)類,而不是將這些代碼一下子灌給你。如果你想用Eclipse來照做,確認(rèn)Eclipse已經(jīng)按照D.5節(jié)描述的步驟設(shè)置過了。然后,創(chuàng)建兩個(gè)Java類(文件->新建->類):BasicFramework和MainApp。確認(rèn)你建有包org.eclipseguide.swt,并是在其中創(chuàng)建它們的。
D.6.1 BasicFramework類
讓我們首先來看看BasicFramework。第一個(gè)部分定義了該類所在的包,然后導(dǎo)入了示例所需的類:
package org.eclipseguide.swt; import org.eclipse.swt.SWT; import org.eclipse.swt.events.*; import org.eclipse.swt.widgets.*;
|
BasicFramework定義為抽象類以確保該類的所有子類都提供dispose()和displayHelpAboutDialog()方法的實(shí)現(xiàn)。在這個(gè)基礎(chǔ)框架中,這些方法作為提醒來使用,以便你記得要清理資源以及要提供你自己的[關(guān)于]對話框。
代碼的其它部分是你很快就要用到的窗口小部件的聲明:
public abstract class BasicFramework { protected Display display; protected Shell shell; protected Menu menuBar, fileSubMenu, helpSubMenu; protected MenuItem fileSubMenuHeader; protected MenuItem fileExit, helpSubMenuHeader; protected MenuItem helpAbout; public abstract void dispose(); public abstract void displayHelpAboutDialog();
|
下面的內(nèi)部類實(shí)現(xiàn)了SelectionListener以便處理選擇事件。它會(huì)被連接到[退出]菜單項(xiàng)(很快就定義)。主要做的是關(guān)閉窗體,也就是中止消息泵,然后調(diào)用dispose()以確保兩個(gè)動(dòng)作銜接在一起:
class FileExitListener implements SelectionListener { public void widgetSelected(SelectionEvent event) { shell.close(); dispose(); } public void widgetDefaultSelected(SelectionEvent event) { shell.close(); dispose(); } }
|
類似的,下一個(gè)內(nèi)部類處理[幫助]菜單的[關(guān)于...]按鈕的選擇事件:
class HelpAboutListener implements SelectionListener { public void widgetSelected(SelectionEvent event) { displayHelpAboutDialog(); } public void widgetDefaultSelected(SelectionEvent event) { displayHelpAboutDialog(); } }
|
注意 在這兩個(gè)偵聽器類中,widgetSelected和widgetDefaultSelected這兩個(gè)方法處理的事件類型不同。widgetSelected處理用戶用鼠標(biāo)選擇窗口小部件的事件,例如,點(diǎn)擊一個(gè)按鈕。widgetDefaultSelected處理當(dāng)按鈕具有焦點(diǎn)時(shí),用戶按[空格]鍵或[回車]鍵所產(chǎn)生的事件。
|
下面你要開始建立SWT體系結(jié)構(gòu)的層次結(jié)構(gòu):
public BasicFramework(String windowTitle) { display = new Display(); shell = new Shell(display); shell.setText(windowTitle);
|
記住,
Display窗口小部件是你的程序與操作系統(tǒng)對話的中轉(zhuǎn)對象。隨即創(chuàng)建Shell并傳遞display給它作為其父部件。該Shell就作為你的頂層窗體,其他所有東西都在其上建立。最后,Shell有一系列的輔助方法:
setMinimized()、
setMaximized()、等等。在這里,你設(shè)置了窗口標(biāo)題。
在SWT中創(chuàng)建一個(gè)全功能菜單欄是一個(gè)復(fù)雜的過程。設(shè)計(jì)者不是只提供一些簡單的類諸如菜單欄、菜單、菜單項(xiàng)、子菜單,而是采用了兩個(gè)類,并依據(jù)所傳遞的樣式來充當(dāng)不同的角色:
menuBar = new Menu(shell, SWT.BAR); fileSubMenuHeader = new MenuItem(menuBar, SWT.CASCADE); fileSubMenuHeader.setText("文件(&F)");
|
第一步是創(chuàng)建菜單欄以供其他所有菜單掛載。你可以通過傳遞樣式參數(shù)SWT.BAR來實(shí)現(xiàn)。然后要?jiǎng)?chuàng)建一個(gè)
掛載點(diǎn),基本上就是個(gè)供菜單掛載的占位符。最后是設(shè)置該占位符顯示在菜單欄上的文字。字母“F”旁的“&”號表明將“F”作為
助記符(訪問該菜單的一個(gè)鍵盤快捷鍵)。使用時(shí),可以按[Alt]鍵激活菜單然后按[F]鍵來選擇該菜單。然后就可以使用光標(biāo)鍵來瀏覽菜單了。
菜單創(chuàng)建工作的下一部分要求你創(chuàng)建在點(diǎn)擊文字[文件]時(shí)顯示的菜單。通過指定DROP_DOWN樣式來把它創(chuàng)建為下拉菜單。(另一個(gè)可用選項(xiàng)是POP_UP,這將會(huì)建立彈出菜單,在右鍵點(diǎn)擊選擇或其它方式中很有用。)然后,把菜單連接到占位符:
fileSubMenu = new Menu(shell, SWT.DROP_DOWN); fileSubMenuHeader.setMenu(fileSubMenu);
|
在創(chuàng)建菜單的最后階段里,你要?jiǎng)?chuàng)建菜單的項(xiàng)目。就像過去一樣,你需要指定菜單項(xiàng)的樣式,不過你有更多的選擇--除了創(chuàng)建一個(gè)普通按鈕式菜單項(xiàng),你還可以創(chuàng)建復(fù)選項(xiàng)目、單選項(xiàng)目,或者是一個(gè)分隔條(用來給你的菜單分段):
fileExit = new MenuItem(fileSubMenu, SWT.PUSH); fileExit.setText("退出(&X)");
|
這些和[文件]菜單的寫法一樣:
helpSubMenuHeader = new MenuItem(menuBar, SWT.CASCADE); helpSubMenuHeader.setText("幫助(&H)"); helpSubMenu = new Menu(shell, SWT.DROP_DOWN); helpSubMenuHeader.setMenu(helpSubMenu); helpAbout = new MenuItem(helpSubMenu, SWT.PUSH); helpAbout.setText("關(guān)于(&A)");
|
下面就要把偵聽器類連接到菜單項(xiàng)了。從這里開始,當(dāng)你點(diǎn)擊[文件]菜單的[退出]項(xiàng)或者[幫助]菜單的[關(guān)于]項(xiàng)時(shí),這些事件就會(huì)被處理:
fileExit.addSelectionListener(new FileExitListener()); helpAbout.addSelectionListener(new HelpAboutListener());
|
下面一行將菜單欄連接到頂層的
shell:
shell.setMenuBar(menuBar); }
|
以下是
BasicFramework類的最后一段代碼。這段很重要,它不僅使頂層窗體(
Shell)顯示在屏幕上,還創(chuàng)建了消息泵--這個(gè)示例的核心。當(dāng)你開發(fā)獨(dú)立運(yùn)行的程序時(shí),記住,沒有了這一部分,你的程序所能做的將非常有限:
public void mainLoop(int hSize, int vSize) { shell.setSize(hSize, vSize); shell.setVisible(true); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } } }
|
D.6.2 MainApp類
現(xiàn)在,我們來看看MainApp類,它擴(kuò)展了BasicFramework類并做了些很有用的工作。像以往一樣,你要將這個(gè)類聲明為org.eclipseguide.swt包的成員,然后導(dǎo)入示例需要的所有類:
package org.eclipseguide.swt;
import java.util.*; import org.eclipse.swt.SWT; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.*;
|
要設(shè)置一個(gè)計(jì)時(shí)器,你要使用java.util.Timer類而不是使用一個(gè)線程。如果你沒用過Java 1.3及以上版本(1.3版是Eclipse運(yùn)行的最低要求),你可能沒用過這個(gè)類。這是一個(gè)簡單便捷的類,它提供了一個(gè)專用的計(jì)時(shí)器線程:
public class MainApp extends BasicFramework { Timer timer; Button button;
|
這個(gè)按鈕放置在程序的主窗體中并顯示時(shí)間。當(dāng)點(diǎn)擊按鈕時(shí),它會(huì)將時(shí)間打印到控制臺(tái)。
下面的內(nèi)部類擴(kuò)展了抽象類TimerTask。基本上,一個(gè)計(jì)時(shí)器任務(wù)就是一個(gè)傳遞到計(jì)時(shí)器的Runnable,它會(huì)以指定間隔執(zhí)行。在run方法中,你要將一個(gè)匿名的runnable增加到事件隊(duì)列里,以便用當(dāng)前時(shí)間來更新按鈕文本。由于并不關(guān)心用戶界面線程什么時(shí)候會(huì)處理它,所以要用asyncExec方法來加上它:
private class ClockUpdateTask extends TimerTask { public void run() { Display.getDefault().asyncExec(new Runnable() { public void run() { button.setText(new Date().toString()); } }); } }
|
這是MainApp類的構(gòu)造函數(shù)。在這里要調(diào)用其父類BasicFramework的構(gòu)造函數(shù)并傳遞標(biāo)題文字:
public MainApp() { super("SWT Example Framework");
|
每個(gè)窗體都可以有一個(gè)布局管理器以控制窗口小部件的位置和大小:
shell.setLayout(new FillLayout(SWT.VERTICAL));
|
有5種已定義好的布局:
填充布局(FillLayout)、
堆布局(StackLayout)、
網(wǎng)格布局(GridLayout)、
表單布局(FormLayout)以及
行布局(RowLayout)。同時(shí)還有一個(gè)
自定義布局(CustomLayout)供你更好的控制窗口小部件的位置。
代碼的下一部分創(chuàng)建了一個(gè)普通按鈕并將它加到了shell窗口上。你目前正在使用填充布局,所以無法設(shè)置按鈕大小。它只會(huì)簡單的擴(kuò)展到全部可用的空間中:
button = new Button(shell, SWT.PUSH);
|
以下代碼展示了將一個(gè)偵聽器連接到一個(gè)窗口小部件的另一途徑--將其與一個(gè)匿名類連接。對于短小的類來說,應(yīng)該優(yōu)先使用匿名類。 除外,建議使用完全類(作為內(nèi)部類或者友好包):
button.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent event) { System.out.println( "Button clicked - time is: " + button.getText()); } public void widgetDefaultSelected(SelectionEvent event) { System.out.println( "Button pressed with default key - time is: " + button.getText()); } });
|
下面就要?jiǎng)?chuàng)建定時(shí)器并安排你的ClockUpdate任務(wù)了,它將在0毫秒的延遲后啟動(dòng),之后每隔1000毫秒(1秒)調(diào)用一次:
timer = new Timer(); timer.scheduleAtFixedRate(new ClockUpdateTask(), 0, 1000);
|
你不需要告訴計(jì)時(shí)器開始工作--程序一創(chuàng)建,它就立即運(yùn)行了。
以下代碼調(diào)用了BasicFramework類的mainloop,在那里會(huì)進(jìn)入事件循環(huán)直至shell被關(guān)閉。那時(shí),就將會(huì)退出程序所運(yùn)行的JVM:
this.mainLoop(300, 200); System.exit(0); }
|
這些代碼創(chuàng)建MainApp類并開始整個(gè)程序的運(yùn)行:
public static void main(String[] args) { new MainApp(); }
|
現(xiàn)在你就要實(shí)現(xiàn)父類的抽象方法了,然而,這些代碼沒有用它們來做任何有用的事情。這種設(shè)計(jì)是為了讓你能把所有聲明過的資源都放入dispose方法中,以便輕松定位所有需要清理的資源:
public void dispose() { System.out.println("Disposing of Resources"); } public void displayHelpAboutDialog() { System.out.println("Display Help About Dialog"); } }
|
我們同樣想象到,你會(huì)把創(chuàng)建你的[關(guān)于]窗體所需的一切東西放進(jìn)displayHelpAboutDialog()方法中。