Buoy 是一個構建在 Swing 之上的免費用戶界面(UI)工具包,它為 UI 開發人員提供了方便性和簡單性。在本文中作者用一個簡單的 fractal 用戶界面程序,介紹了 Buoy 可以做什麼、為什麼這麼做。第一次嘗試用 Java 語言構建簡單的用戶界面時,我對 Swing 接口的復雜性感到有些驚奇。老實說,有點想打退堂鼓。最近,一個朋友向我提到,他使用的渲染程序 Art of Illusion(請參閱 參考資料)基於一個不同的工具包:Buoy。推薦它的原因之一是它的界面更友好。當他第一次提到它時,我以為他在談 "BUI",而它與 GUI 這個名字的相似是故意的。在這裡 B 代表 better(更好),但是名字 Buoy 並不是縮寫。
Buoy 是免費的。實際上,它是公共的東西。它並沒有在某個開放程度合理的許可下發布,實際上它根本不受任何許可控制。這意味著在任何用 Java 語言編寫的能夠運行 Buoy 的項目中都可以使用 Buoy,而不用考慮許可問題。因為提供了完整的源代碼,所以這個工具包很輕易修改和擴展。本文基於 Buoy 1.3 發行版,要求讀者對 Swing 有基本的了解,雖然不了解也能對付過去。
示例程序
我曾經嘗試用 Swing 構建的第一個應用程序最後以失敗告終。為了看出工具包之間的對比情況,我決定使用 Buoy 來構建這同一個程序。文章中的代碼示例全部來自該程序的 Buoy 版本。程序生成了一些分形,具體地說,是迭代的分形。基本思想很簡單:在平面上定義一系列的線條區段,從(0,0) 到(1,0),圍繞任意一個單位線條定位。繪制這些區段之後,繪制同一套變形線條,用這個區段作為單位向量。做起來比說的更輕易,就像在圖 1 中看到的。
圖 1. 分形編輯器中的分形
這個程序的界面相當簡單。它有一些界面小部件,有一個畫布,在畫布上繪制漂亮的圖片,還支持用鼠標操縱圖片。實際上,必須要做的全部工作就是操縱構成原始曲線的點,原始曲線會迭代地繪制出來。界面還有一個最小化的菜單;它可以打開和關閉文件,關閉窗口,或者把當前圖像保存為 PNG 格式的文件。雖然簡單,但是這個界面簡要地提供了一個 Buoy 小部件的合理示例,還有相當數量對事件處理系統的體驗。
程序實際的核心代碼 —— 分形生成器 —— 已經寫好了,這把這個示例變成一個很好的測試程序。當然,在更新它的過程中,我也發現並且修補了一些 bug。
發行包中包含示例程序的源代碼,還有編譯好的類文件和 Buoy 的 JAR 文件(單擊本文頂部或底部的 Code 圖標,下載 factal.tar)。包中還包含一個叫做 frac 的目錄,裡面包含一些示例分形。假如使用一台 UNIX 風格的機器,在路徑中有 Java 編譯器,那麼只要運行 make 就能運行它。 <!-- frame contents -->
<!-- /frame contents -->
否則,需要設置 classpath 包含當前路徑和 Buoy 的 JAR 文件所在的目錄,然後運行 FractalViewer 類。在 Windows 系統上,正確的命令行應當是 java -classpath .;Buoy.jar FractalViewer。
sed -e s/J/B/g 在第一次深入研究代碼時,也許會形成這樣的印象:把 Swing 代碼轉換成 Buoy 代碼簡單得就像把 UI 元素名稱中的字母 J 換成 B 一樣簡單。例如, FractalViewer 類不再擴展 JFrame;它現在擴展的是 BFrame。主要的小部件名稱也可以照此推測得到。Spinner 和 slider 像以前一樣有相同的名字,只是換了一個字母。 MenuBar(菜單條) 仍然由 Menus(菜單)構成,菜單則容納 MenuItems。
有些命名轉換略有不同。在 Swing 引用 BorderLayout 的地方,Buoy 有 BorderContainer。一般來說,Buoy 的命名轉換相當統一,雖然不總是與 Swing 的命名一樣。一個明顯的區別是 Buoy 幾乎組合了容器和布局治理器的概念;每種容器類型都知道自己如何布局。這大大簡化了設計。例如,在分形生成器中使用的 LabelWidget 類是一個 BorderContainer;在 Swing 中,這可能是一個帶有 BorderLayout 布局治理器的 JPanel。
但是,兩者還是有許多相似之處。這對適應新東西有很大幫助。更重要的是,Buoy 構建在 Swing 之上。這意味著,一般來說,假如需要做的事不能輕松地用 Bouy 完成時,可以把 Buoy 對象傳遞給它包裝的 Swing 對象。對於這種情況,假如想訪問一些沒有 Buoy 對應物的 Swing 對象,可以簡單地把它包裝在 AWTWidget 對象中,這個對象提供了非常薄的包裝器,通過它,不僅 Buoy 自己的小部件,而且所有的小部件都能訪問 Buoy 的小部件 API。例如,假如發現確實需要 GridBagLayout,可能就需要這樣做。
例如,FractalPanel 類是一個 AWTWidget。在早期設計中, 它是 JPanel 的子類, 但實際上我並不需要 JPanel 代碼。相反,我構建了包裝定制類的類 FractalCanvas, 它本身是普通的 Canvas 類的一個子類。把它變成一個 AWTWidget,就可以在它上面利用 Buoy 高效的事件處理機制。
事件處理代碼非常簡單。在按下鼠標按鈕時,通過 addEventLink() 的魔力,Buoy 發送一個新的 MousePressedEvent 事件到 mousePressed() 函數。我忽略了按下哪個按鈕這個問題,只考慮按住 shift 單擊或普通單擊。普通單擊選擇最靠近的點,而按住 shift 單擊則重新把顯示居中。然後,假如鼠標移動,那麼每次 Buoy 注重到移動時都會開始發送 MouseDraggedEvent 事件。在處理這些事件時,FractalPanel 會生成自己的事件。