簡(jiǎn)單完成Java用戶界面編程
發(fā)表時(shí)間:2024-06-08 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]Buoy 是一個(gè)構(gòu)建在 Swing 之上的免費(fèi)用戶界面(UI)工具包,它為 UI 開發(fā)人員提供了方便性和簡(jiǎn)單性。在本文中作者用一個(gè)簡(jiǎn)單的 fractal 用戶界面程序,介紹了 Buoy 可以做什么、為什么這么做。第一次嘗試用 Java 語(yǔ)言構(gòu)建簡(jiǎn)單的用戶界面時(shí),我對(duì) Swing 接口的復(fù)雜性感到有...
Buoy 是一個(gè)構(gòu)建在 Swing 之上的免費(fèi)用戶界面(UI)工具包,它為 UI 開發(fā)人員提供了方便性和簡(jiǎn)單性。在本文中作者用一個(gè)簡(jiǎn)單的 fractal 用戶界面程序,介紹了 Buoy 可以做什么、為什么這么做。
第一次嘗試用 Java 語(yǔ)言構(gòu)建簡(jiǎn)單的用戶界面時(shí),我對(duì) Swing 接口的復(fù)雜性感到有些驚訝。老實(shí)說(shuō),有點(diǎn)想打退堂鼓。最近,一個(gè)朋友向我提到,他使用的渲染程序 Art of Illusion(請(qǐng)參閱 參考資料)基于一個(gè)不同的工具包:Buoy。推薦它的原因之一是它的界面更友好。當(dāng)他第一次提到它時(shí),我以為他在談 "BUI",而它與 GUI 這個(gè)名字的相似是故意的。在這里 B 代表 better(更好),但是名字 Buoy 并不是縮寫。
Buoy 是免費(fèi)的。實(shí)際上,它是公共的東西。它并沒(méi)有在某個(gè)開放程度合理的許可下發(fā)布,實(shí)際上它根本不受任何許可控制。這意味著在任何用 Java 語(yǔ)言編寫的能夠運(yùn)行 Buoy 的項(xiàng)目中都可以使用 Buoy,而不用考慮許可問(wèn)題。因?yàn)樘峁┝送暾脑创a,所以這個(gè)工具包很容易修改和擴(kuò)展。本文基于 Buoy 1.3 發(fā)行版,要求讀者對(duì) Swing 有基本的了解,雖然不了解也能對(duì)付過(guò)去。
示例程序
我曾經(jīng)嘗試用 Swing 構(gòu)建的第一個(gè)應(yīng)用程序最后以失敗告終。為了看出工具包之間的對(duì)比情況,我決定使用 Buoy 來(lái)構(gòu)建這同一個(gè)程序。文章中的代碼示例全部來(lái)自該程序的 Buoy 版本。程序生成了一些分形,具體地說(shuō),是迭代的分形;舅枷牒芎(jiǎn)單:在平面上定義一系列的線條區(qū)段,從(0,0) 到(1,0),圍繞任意一個(gè)單位線條定位。繪制這些區(qū)段之后,繪制同一套變形線條,用這個(gè)區(qū)段作為單位向量。做起來(lái)比說(shuō)的更容易,就像在圖 1 中看到的。
圖 1. 分形編輯器中的分形 這個(gè)程序的界面相當(dāng)簡(jiǎn)單。它有一些界面小部件,有一個(gè)畫布,在畫布上繪制漂亮的圖片,還支持用鼠標(biāo)操縱圖片。實(shí)際上,必須要做的全部工作就是操縱構(gòu)成原始曲線的點(diǎn),原始曲線會(huì)迭代地繪制出來(lái)。界面還有一個(gè)最小化的菜單;它可以打開和關(guān)閉文件,關(guān)閉窗口,或者把當(dāng)前圖像保存為 PNG 格式的文件。雖然簡(jiǎn)單,但是這個(gè)界面簡(jiǎn)要地提供了一個(gè) Buoy 小部件的合理示例,還有相當(dāng)數(shù)量對(duì)事件處理系統(tǒng)的體驗(yàn)。
程序?qū)嶋H的核心代碼 —— 分形生成器 —— 已經(jīng)寫好了,這把這個(gè)示例變成一個(gè)很好的測(cè)試程序。當(dāng)然,在更新它的過(guò)程中,我也發(fā)現(xiàn)并且修補(bǔ)了一些 bug。
[page_break] 發(fā)行包中包含示例程序的源代碼,還有編譯好的類文件和 Buoy 的 JAR 文件(單擊本文頂部或底部的 Code 圖標(biāo),下載 factal.tar)。包中還包含一個(gè)叫做 frac 的目錄,里面包含一些示例分形。如果使用一臺(tái) UNIX 風(fēng)格的機(jī)器,在路徑中有 Java 編譯器,那么只要運(yùn)行 make 就能運(yùn)行它。否則,需要設(shè)置 classpath 包含當(dāng)前路徑和 Buoy 的 JAR 文件所在的目錄,然后運(yùn)行 FractalViewer 類。在 Windows 系統(tǒng)上,正確的命令行應(yīng)當(dāng)是 java -classpath .;Buoy.jar FractalViewer。
sed -e s/J/B/g
在第一次深入研究代碼時(shí),也許會(huì)形成這樣的印象:把 Swing 代碼轉(zhuǎn)換成 Buoy 代碼簡(jiǎn)單得就像把 UI 元素名稱中的字母 J 換成 B 一樣簡(jiǎn)單。例如, FractalViewer 類不再擴(kuò)展 JFrame;它現(xiàn)在擴(kuò)展的是 BFrame。主要的小部件名稱也可以照此推測(cè)得到。Spinner 和 slider 像以前一樣有相同的名字,只是換了一個(gè)字母。 MenuBar(菜單條) 仍然由 Menus(菜單)構(gòu)成,菜單則容納 MenuItems。
有些命名轉(zhuǎn)換略有不同。在 Swing 引用 BorderLayout 的地方,Buoy 有 BorderContainer。一般來(lái)說(shuō),Buoy 的命名轉(zhuǎn)換相當(dāng)統(tǒng)一,雖然不總是與 Swing 的命名一樣。一個(gè)明顯的區(qū)別是 Buoy 幾乎組合了容器和布局管理器的概念;每種容器類型都知道自己如何布局。這大大簡(jiǎn)化了設(shè)計(jì)。例如,在分形生成器中使用的 LabelWidget 類是一個(gè) BorderContainer;在 Swing 中,這可能是一個(gè)帶有 BorderLayout 布局管理器的 JPanel。
但是,兩者還是有許多相似之處。這對(duì)適應(yīng)新東西有很大幫助。更重要的是,Buoy 構(gòu)建在 Swing 之上。這意味著,一般來(lái)說(shuō),如果需要做的事不能輕松地用 Bouy 完成時(shí),可以把 Buoy 對(duì)象傳遞給它包裝的 Swing 對(duì)象。對(duì)于這種情況,如果想訪問(wèn)一些沒(méi)有 Buoy 對(duì)應(yīng)物的 Swing 對(duì)象,可以簡(jiǎn)單地把它包裝在 AWTWidget 對(duì)象中,這個(gè)對(duì)象提供了非常薄的包裝器,通過(guò)它,不僅 Buoy 自己的小部件,而且所有的小部件都能訪問(wèn) Buoy 的小部件 API。例如,如果發(fā)現(xiàn)確實(shí)需要 GridBagLayout,可能就需要這樣做。
例如,F(xiàn)ractalPanel 類是一個(gè) AWTWidget。在早期設(shè)計(jì)中, 它是 JPanel 的子類, 但實(shí)際上我并不需要 JPanel 代碼。相反,我構(gòu)建了包裝定制類的類 FractalCanvas, 它本身是普通的 Canvas 類的一個(gè)子類。把它變成一個(gè) AWTWidget,就可以在它上面利用 Buoy 高效的事件處理機(jī)制。
事件處理代碼非常簡(jiǎn)單。在按下鼠標(biāo)按鈕時(shí),通過(guò) addEventLink() 的魔力,Buoy 發(fā)送一個(gè)新的 MousePressedEvent 事件到 mousePressed() 函數(shù)。我忽略了按下哪個(gè)按鈕這個(gè)問(wèn)題,只考慮按住 shift 單擊或普通單擊。普通單擊選擇最靠近的點(diǎn),而按住 shift 單擊則重新把顯示居中。然后,如果鼠標(biāo)移動(dòng),那么每次 Buoy 注意到移動(dòng)時(shí)都會(huì)開始發(fā)送 MouseDraggedEvent 事件。在處理這些事件時(shí),F(xiàn)ractalPanel 會(huì)生成自己的事件。
近觀 PointChangedEvent
為了讓一些討論更加具體,請(qǐng)來(lái)看 PointChangedEvent。這是一個(gè)試驗(yàn)性的類,如果不喜歡它,那也只能怪老天了。這個(gè)類的想法是:讓一個(gè)類來(lái)表示狀態(tài)點(diǎn)中的變化。編輯器跟蹤“當(dāng)前”點(diǎn) —— 也就是編輯器小部件目前正在編輯的點(diǎn)?梢杂眠@些小部件或在分形面板中單擊選擇新的點(diǎn),選擇的是最靠近的點(diǎn)。
我得出這樣一個(gè)結(jié)論:在代碼中,大概有三類涉及到點(diǎn)的事件需要從一個(gè)類發(fā)送到另一個(gè)類。
一個(gè)是改變某個(gè)點(diǎn)的特征: POINT 事件類型。如果由編輯器發(fā)送,就是告訴分形改變?cè)途條上的點(diǎn),并要求重畫線條。如果由分形發(fā)送,則是告訴編輯器剛剛選中的點(diǎn)的特性。
下一個(gè)是選擇某個(gè)點(diǎn)。可以按索引或位置進(jìn)行選擇。所以,如果只提供了索引或位置,那么構(gòu)造函數(shù)會(huì)認(rèn)為意圖是填充其他值。有一點(diǎn)特殊的地方,點(diǎn)索引 -1 用來(lái)表示沒(méi)有選中的點(diǎn),所以必須用 -2表示編輯器正在尋找指定位置的點(diǎn)。這可能不漂亮,但是有效。
有點(diǎn)意思的是 Fractal 類響應(yīng) SELECT 事件的方式。如果成功地選擇了一個(gè)點(diǎn),就會(huì)發(fā)回一個(gè)新的 POINT 類型的 PointChangedEvent 事件,如清單 1 所示。
清單 1. 用事件回答事件
case PointChangedEvent.SELECT:
if (e.getIndex() >= -1)
selectPoint(e.getIndex());
else
selectPoint(e.getPoint());
// just in case they don't know
event(new FractalChangedEvent(FractalChangedEvent.SIZE, size));
if (selectedPoint >= 0 && selectedPoint < size)
event(new PointChangedEvent(selectedPoint, points[selectedPoint]));
else
event(new PointChangedEvent(selectedPoint, null));
event(new FractalChangedEvent(FractalChangedEvent.REDRAW));
break;
最后,移動(dòng)點(diǎn)是一個(gè)特殊情況,如果不需要改變點(diǎn)的其他屬性(例如顏色),那么所要處理的就是位置。這就是 MOVE 事件類型。在效果上,它與 POINT 事件類型效果很像,但它不需要事件生成器(通常是 FractalPanel 類)去關(guān)心那些它根本不知道的屬性。
[page_break] INSERT 和 DELETE 事件類型只有部分相關(guān),可能應(yīng)當(dāng)屬于 FractalChangedEvent 事件。
事件處理
正如已經(jīng)開始看到的,事件處理是 Buoy 與 Swing 最明顯的不同之處。事件處理提供了大量靈活性。Buoy 本身的事件集相當(dāng)豐富,且允許您挑選自己感興趣的事件,從任何小部件向其他對(duì)象發(fā)送事件。例如,如果想在 Swing 中捕獲鼠標(biāo)事件,捕獲事件的類需要實(shí)現(xiàn) MouseListener 接口。這個(gè)接口有 5 個(gè)函數(shù)需要實(shí)現(xiàn),即使它們就是擺設(shè)也必須實(shí)現(xiàn)。而且必須使用接口提供的函數(shù)名稱。更糟的是,函數(shù)必須是偵聽器接口的公共部分;要么把這作為公共接口的一部分公開,要么創(chuàng)建一個(gè)什么都不做、只是包裝事件偵聽器代碼的內(nèi)部類。
在 Buoy 中,每個(gè)小部件都是 EventSource 。這意味著可以從每個(gè)小部件偵聽事件。什么類型的事件呢?任何類型都可以。關(guān)鍵的函數(shù)是 addEventLink()。這允許您指定類、偵聽器以及可選的方法。每當(dāng) EventSource 分派這個(gè)類或它的子類的事件時(shí),偵聽器都會(huì)接收到事件,要么是通過(guò)一個(gè)叫做 processEvent()的方法,要么是通過(guò)在開始調(diào)用 addEventLink() 時(shí)提供的方法名稱。提供的函數(shù)不能接受參數(shù),也不能接受與指定事件類型兼容的類的對(duì)象;父類和接口可以。
這是一個(gè)方便的設(shè)置?梢园巡煌氖录酚傻讲煌暮瘮(shù)或相同的函數(shù)。例如,MousePressedEvent 和 MouseReleasedEvent 會(huì)被分別處理。在示例程序中,鼠標(biāo)的按下、釋放和拖動(dòng)分別有不同的線程,如清單 2 所示。注意,這遠(yuǎn)遠(yuǎn)超過(guò) Swing 的 MouseListener 所能做的。如果用 Swing 編程的話,就需要實(shí)現(xiàn) MouseListener 和 MouseMotionListener 這兩個(gè)接口。
清單2. 只挑感興趣的事件
this.addEventLink(MousePressedEvent.class, this, "mousePressed");
this.addEventLink(MouseReleasedEvent.class, this, "mouseReleased");
this.addEventLink(MouseDraggedEvent.class, this, "mouseDragged");
[...]
public void mouseReleased(WidgetMouseEvent ev) {
lastCenter = null;
dispatchEvent(new FractalChangedEvent(FractalChangedEvent.SLOW));
setAntiAliasing(true);
}
mouseReleased() 函數(shù)只有最少的工作要做。它只是在 mousePressed() 函數(shù)之后進(jìn)行清理,告訴 Fractal 對(duì)象到了開始全面重繪的時(shí)候了。
Buoy 的事件處理還有另外一個(gè)有趣的特性。如果愿意的話,可以創(chuàng)建新的事件類型。一個(gè)事件類型就是一個(gè)類。確實(shí)如此。它甚至不需要繼承任何類或?qū)崿F(xiàn)什么。它就是一個(gè)類。如果這個(gè)類的對(duì)象被發(fā)送到 dispatchEvent(),那么它或它的父類的偵聽器就會(huì)被調(diào)用。在 Swing 中也可以創(chuàng)建新的事件類型,但是完全要自己進(jìn)行;必須設(shè)計(jì) Listener 接口,還要編寫自己的代碼生成事件并偵聽事件。在示例程序中,設(shè)計(jì)了 Fractal 類,演示了可以相對(duì)容易地把事件處理功能加到任何原有的類中。只需要聲明一個(gè) FractalViewer 類用來(lái)添加偵聽器的事件源 EventSource。FractalViewer 類就會(huì)把來(lái)自事件源(例如 FractalEditor)的事件鏈接設(shè)置到它們的偵聽器,如清單 3 所示。
清單3. 綁定
private void tieEvents() {
// Set up event handling relations.
addEventLink(WindowResizedEvent.class, this, "layoutChildren");
addEventLink(WindowResizedEvent.class, panel, "repaint");
tieControlEvents();
tieFractalEvents();
tiePanelEvents();
}
定制事件類一般是為了表示用戶行為。在 Buoy 中,一般只通過(guò)用戶行為,而不是系統(tǒng)接口生成事件 —— 除非自己想顯式地調(diào)用 dispatchEvent() 自行生成事件。當(dāng)分形對(duì)象以某種會(huì)造成字段更新的方式變化的時(shí)候,所有部件的控制面板都會(huì)得到通知。這樣,我們發(fā)明一個(gè)新類 ParameterChangedEvent,用它表示參數(shù)已經(jīng)變化。或者,如果變化的是選中的點(diǎn)的位置或是索引,就發(fā)送一個(gè)新的 PointChangedEvent。如果行為足夠明顯的話,那么事件處理器甚至不需要接受參數(shù)。作為事件處理的一個(gè)示例,請(qǐng)看清單 4,它演示了 FractalEditor 的 parameterChanged() 方法的開始部分。
清單 4. 參數(shù)發(fā)生了變化
void parameterChanged(ParameterChangedEvent ev) {
FractalParameters p = ev.getParams();
int v = ev.getValue();
switch (ev.getType()) {
case ParameterChangedEvent.ALL:
maxSlider.setValue(p.getMaxIterations());
minSlider.setMaximum(p.getMaxIterations());
minSlider.setValue(p.getMinIterations());
maxSlider.setMinimum(p.getMinIterations());
zoomSlider.setValue(p.getZoom());
break;
[...]
在這個(gè)例子中,用事件處理系統(tǒng)把各種信息前后傳遞。在以前的版本中,每個(gè)類都有對(duì)其他每個(gè)類的引用,而且亂七八糟的 get 方法是按天排序的。而在目前的版本中,Buoy 的事件處理系統(tǒng)被用來(lái)處理各種通知。例如,F(xiàn)ractalChangedEvent 類可以用來(lái)讓代碼的其他部分知道對(duì)分形的修改,可能是點(diǎn)的數(shù)量變化(編輯器用點(diǎn)的數(shù)量為點(diǎn)選擇器定義正確的 SpinnerNumberModel),或者是需要重繪的通知
[page_break] 清單 5. 顯然到了重繪的時(shí)候
public void fractalChanged(FractalChangedEvent e) {
switch (e.getType()) {
case FractalChangedEvent.REDRAW:
repaint();
break;
}
}
Buoy 的文檔詳細(xì)討論了 Swing 事件模型與 Buoy 事件模型的差異,以及這些差異的原因。有很好的理由,而且 Buoy 的模型通常會(huì)導(dǎo)致更小、更清晰的代碼。當(dāng)然,仍然可以做多余的或愚蠢的事情,就像在任何系統(tǒng)中都可以做的那樣,但是至少在做這些事情的時(shí)候有一個(gè)干凈漂亮的界面。
學(xué)習(xí)曲線
我曾經(jīng)觀察到,學(xué)習(xí)使用一個(gè) GUI 工具,一下午的時(shí)間還不夠長(zhǎng)。對(duì)于 Buoy,我大概需要 6 個(gè)小時(shí)或者差不多一整個(gè)工作日。我確實(shí)從更有經(jīng)驗(yàn)的 Buoy 用戶那里得到了很棒的幫助。以前學(xué)習(xí) Swing 的經(jīng)驗(yàn)也是有幫助的,但實(shí)際上,我并不認(rèn)為 Swing 的經(jīng)驗(yàn)是必需的。Buoy 的文檔相當(dāng)好,而它的簡(jiǎn)單性確實(shí)有幫助。對(duì)于基本的 UI 事物,沒(méi)有太多要學(xué)的東西。
Buoy 的文檔并不像 Swing 文檔那樣完整,但是覆蓋了許多細(xì)節(jié),而且非常好。另外,源代碼也在那兒,所以回答一些關(guān)于界面的簡(jiǎn)單問(wèn)題非常容易。具有更完整的文檔當(dāng)然是好事。但是,既然這個(gè)項(xiàng)目放在 SourceForge 上,所以如果您愿意,您可以編寫更多的東西為它做貢獻(xiàn)。
Buoy 的學(xué)習(xí)曲線比起 Swing 是一個(gè)很大的優(yōu)勢(shì)。用相當(dāng)簡(jiǎn)單的界面就能讓大多數(shù)界面小部件正確工作。要使用 Buoy 文檔中的一個(gè)示例:在 Swing 中,JList 要求要么使用靜態(tài)列表,要么構(gòu)建一個(gè)實(shí)現(xiàn) ListModel 接口的新類。在 Buoy 中,只需向列表中添加項(xiàng)目;在大多數(shù)常見情況下,艱巨的工作已經(jīng)由 Buoy 替您做了。
Buoy 相當(dāng)小。完整的發(fā)行包中包含源代碼、JAR文件和文檔,總共不到 1 MB。代碼的組織良好,可以容易地找到任何特定的代碼段,如果需要調(diào)整設(shè)計(jì),也不困難。
Bug
盡管 Buoy 是一個(gè)穩(wěn)定、有用的系統(tǒng),但并不是一個(gè)絕對(duì)完美的東西。偶爾在明顯選擇很合理的地方它也會(huì)有奇怪的表現(xiàn),產(chǎn)生令人驚訝的行為。如果考慮用 Buoy 來(lái)完成一個(gè)實(shí)際的項(xiàng)目,就需要了解 bug:它們的普遍程度、嚴(yán)重性,以及克服它們的難度。
在開發(fā)這個(gè)應(yīng)用程序的過(guò)程中,我碰到一些事情,當(dāng)時(shí)看起來(lái)像是 bug。但不全是。有一些可能是文檔中的 bug —— 在這些情況中,代碼的行為不是預(yù)期的,但是卻非常合理。實(shí)際上,我可以非?隙ǎ瑥膶(shí)質(zhì)上講并不是 Buoy 中的 bug,但它們確實(shí)呈現(xiàn)了在調(diào)試 Buoy 應(yīng)用程序時(shí)可能會(huì)遇到一些事情。在調(diào)試了幾天代碼之后,我可以非?隙ǎ矣龅降拿總(gè)明顯的 bug,要么是我的錯(cuò)誤,要么是我不太喜歡的底層 Swing 中的設(shè)計(jì)決策。可以肯定地說(shuō),在 Swing 中不可能避免這些問(wèn)題。
滾動(dòng)條刻度
早期我最常遇到的一個(gè) bug 是處理標(biāo)尺中的刻度標(biāo)記的時(shí)候。最初,我無(wú)法得到在它們上面顯示的標(biāo)簽。
清單 6. 讓人郁悶的滾動(dòng)條代碼
minSlider.setShowLabels(true);
minSlider.setMajorTickSpacing(2);
清單 6 中的代碼不起作用?梢钥吹,標(biāo)簽只是在設(shè)置了刻度間距后才顯示。如果在告訴滾動(dòng)條顯示標(biāo)簽之前沒(méi)有設(shè)置刻度間距,它不顯示任何刻度就結(jié)束了。更微妙的是,隨后也不能改變刻度間距;改變刻度間距的嘗試沒(méi)有效果。但是,這實(shí)際不是 Buoy 的 bug,而是 Swing 的工作方式。由于 BSlider 類只是把請(qǐng)求傳遞給 JSlider,所以責(zé)怪 Buoy 是不公平的。
[page_break] 一個(gè)更微妙、也與底層 JSlider 的毛病有關(guān)的 bug 發(fā)生在對(duì)齊刻度的行為上。 BSlider 的構(gòu)造函數(shù)把次要刻度設(shè)為5,把主刻度設(shè)為 20 —— 相對(duì)于默認(rèn)尺寸 100 來(lái)說(shuō)這兩個(gè)值是合理的。但是,當(dāng)用 1-10 的范圍創(chuàng)建滾動(dòng)條時(shí),卻看不到次要刻度,因此只能把主刻度間距值設(shè)為 1。結(jié)果產(chǎn)生一個(gè)刻度值為 1-10 的滾動(dòng)條,而且只停留在 1 和 6 處;對(duì)齊刻度的行為妨礙了采用其他的值,因?yàn)閷?duì)齊到了次要刻度而不是主刻度。
雖然這個(gè)問(wèn)題源自 JSlider 的實(shí)現(xiàn),但卻在 Buoy 的默認(rèn)行為中發(fā)生了,在即將發(fā)布的 1.4 發(fā)行版中會(huì)修復(fù)它。
注意,這對(duì)我是個(gè)問(wèn)題的惟一原因是,示例程序要不斷地更新一些滾動(dòng)條的范圍。例如,如果有一個(gè)線條區(qū)段,只允許進(jìn)行最多 50 次迭代,那么要在滾動(dòng)條上標(biāo)上每個(gè)數(shù)字的工作量可能有點(diǎn)多。另一方面,如果只允許少數(shù)迭代,那么遺漏某些數(shù)字看起來(lái)就不好了。在一個(gè)滾動(dòng)條范圍不常更新的界面,還是很方便的。
菜單快捷鍵
Buoy 用字符或鍵盤事件(KeyEvent)方便地為菜單快捷鍵提供了構(gòu)造函數(shù)。在第一次測(cè)試它時(shí),我沒(méi)法讓它工作?雌饋(lái)必須使用小寫字母;但在調(diào)用構(gòu)造函數(shù)時(shí)必須用 'W' 代替 'w',如清單 7 所示。
清單 7. 添加 close 菜單項(xiàng)
mi = new BMenuItem("Close", new Shortcut('W'));
mi.setActionCommand("close");
mi.addEventLink(CommandEvent.class, this, "menuEvent");
fileMenu.add(mi);
這樣可能必須要處理 Java 5.0 SDK 與 1.42 不小心模糊重復(fù)的地方。表面來(lái)看,如果把大寫字母?jìng)鬟f給構(gòu)造器,所做的事情正與期望的一樣。底層的問(wèn)題 —— JVM 要用哪一套鍵或修飾符來(lái)表示 Ctrl-? —— 還需要一個(gè)小的自由的庫(kù)才能完全解決。
文件選擇
出于一些不明顯的原因,在 Mac 系統(tǒng)上啟動(dòng)新的文件選擇器時(shí),Buoy 默認(rèn)啟動(dòng)的是根目錄。我做了一個(gè)詳盡的 bug 報(bào)告,是關(guān)于它看起來(lái)是怎樣遺漏大量文件的,但是隨后我就認(rèn)識(shí)到我已經(jīng)把我的主目錄從 /Users/seebs 移動(dòng)到了 /Volumes/Home/seebs,而文件選擇器確實(shí)顯示了磁盤上的東西。分?jǐn)?shù):Buoy 1,Seebs 0。
我仍然想知道為什么它要從文件系統(tǒng)的根開始。這也許是 JVM 的 Mac 實(shí)現(xiàn)的毛病。
結(jié)束語(yǔ)
Buoy 遵循著名的古老的 UNIX 哲學(xué):百分之十的工作解決百分之九十的問(wèn)題。Buoy 并不想為所有人解決所有的問(wèn)題,但是它可以完成界面用戶或設(shè)計(jì)師需要的大部分工作。它擁有可能是最好的許可條款,而且還在不斷發(fā)展。最好的是,如果發(fā)現(xiàn)它不能讓您做自己確實(shí)需要做的事情,您可以隨心所欲地研究它、修改它,要么修改 Buoy 的源代碼,要么調(diào)用 getComponent() 并編寫自己的 Swing 代碼。
如果覺(jué)得較大的 UI 工具包太可怕,那么 Buoy 是個(gè)不錯(cuò)的選擇。它可以讓簡(jiǎn)單的 UI 繼續(xù)簡(jiǎn)單,把復(fù)雜的代碼留到需要的時(shí)候。在實(shí)踐中,對(duì)于少數(shù) Swing 比 Buoy 有優(yōu)勢(shì)的情況,直接在 Buoy 構(gòu)建的程序中編寫少數(shù)代碼就能處理。這是一個(gè)讓我值得花時(shí)間用 Java 進(jìn)行 UI 編程的工具包。