概述Swing核心組件
UI Delegate
如果說模型接口是最編寫自定義組件最重要的部分,那么UI delegate則是最復雜的部分。主要問題在于:如何編寫繪制邏輯使得他在所有的look and feels上都一致?有時,除非你編寫每個look and feels所對應的UI delegate(像 SwingX project 就是通過這種方式),否則是無法實現的。但是,在某些情況下你會發現你可以通過組裝現有的Swing核心組件來達到這種仿真的效果,在后面的部分,UI delegates代碼將要注意那些平臺相關的設置比如說顏色,字體和抗鋸齒。
在" Enhancing Swing Applications" 一文中,作者描述了通過的繼承定義好的look and feels的簡單UI delegate實現的樣板代碼。以install*和unstall*方法開始,如果你不打算使用他們,第三方的look and feel 可能會在基本的功能基礎上增加一些額外的功能(舉個例子來說,在slider上增加鼠標滾輪滾動功能)
在我們這個例子中,我們可以看到這個自定義組件包含一個slider和一組labels(包括圖標和文本),由于所有的 JComponent都是容器類,我們可以輕松地通過在我們的installComponents方法中增加JSlider和JLabel(每個 label是一個選項control point)組件來達到這種仿真的虛擬界面效果(別忘了在uninstallComponents方法中移除他們)。通過重用Swing核心組件的方式,我們的自定義組件能夠使其在swing核心LAF和第三方LAF下都保持一致。
當我們增加附屬組件時,為了在創建和縮放時定位這些附屬組件,我們需要實現自定義的LayoutManager。這是一個非常簡單的工作(甚至有點乏味):這些range在右側縱向并排排列,相鄰的range則根據其自身的weight值擁有相應空間的垂直區域,滑標則占有全部垂直空間,***個和***一個選項(control points)作為slider的起點和終點。
注意這個特殊的實現非常的困難并且幾乎不可能使用單一的UI delegate來完成,舉例來說,一些LAFs使用了native Api 來繪制各自的控制(像滑標的滑道與滑塊)。一些第三方的LAF可能不遵循UIManager中的設置而是提供自己的顏色和自定義的Api
現在回到我們的實現(使用JSlider),我們現在面臨一個有趣的問題:滑標可以通過設置snapToTicks的值來決定是否自動對齊到最近的滑塊刻度。這個行為控制通過在BasicSliderUI delegate中安裝mouse montion監聽器實現。我們要怎么做?其中一個選擇是移除這個監聽器并且安裝上我們所提供的實現。另一個方法是提供自定義的 BoundedRangeModel實現,當設置非關聯的range時修改它的值(value)。***種方法并非***-你無法信賴其他LAF下特殊的 SliderUI delegate實現邏輯,有的實現甚至不會去調用父類的方法。第二種方法稍微好一點,不過我們選擇另一種方法實現,原因稍后說明。
我們的實現對這些附屬組件使用類似樹/表的單元格渲染模式(Cell renderer),slider只是用來渲染并且不獲取任何事件(參考CellRendererPane)。這能使我們從LAF繪圖和鼠標自定義事件中獲益。在我們這個特殊的例子中,如果用戶在slider滑塊外點擊鼠標。我們通過直接設置相應的range的值 (value)來代替原本的向鼠標點擊方向滾動一個”塊”,這就是我們為什么不使用前面提到的第二種方法的原因:我們自定義的鼠標監聽器轉換鼠標點擊轉換相應的range(相鄰的或非關聯的)并且設置他們的值,一旦這個唯一的監聽器被安裝到組件上(指這個CellRendererPane,而這個 slider 只是一個”橡皮圖章”),我們可以保證沒有其他的監聽器阻礙我們的代碼。
倘若我們使用單元格渲染面板,我們需要覆蓋掉它的paint方法來繪制真正的滑塊。我們并沒有繪制那些選項標簽因為他是這個組件“真正的”子組件,注意滑塊的繪制在一個單獨的方法中完成,這樣可以允許第三方的LAF只覆蓋的這個滑塊的繪制邏輯而不是改變整個繪制邏輯。
- @Override
- publicvoidpaint(Graphicsg,JComponentc){
- super.paint(g,c);
- this.paintSlider(g);
- }
- protectedvoidpaintSlider(Graphicsg){
- RectanglesliderBounds=sliderRendererPane.getBounds();
- this.sliderRendererPane.paintComponent(g,this.slider,
- this.flexiSlider,sliderBounds.x,sliderBounds.y,
- sliderBounds.width,sliderBounds.height,true);
- }
測試應用程序
現在我們擁有了一個完整的自定義滑標組件,是時候該測試它了。這個測試應用程序創建了一個滑標,它包含一些相鄰的和非關聯的range,并為其注冊了改變監聽器(change listener)。一旦發生改變事件,我們計算圖標的尺寸比例并繪制它(該圖標使用Tango Desktop Project的SVG-to-Java2D converte來轉換,具體參考 Transcoding SVG to Pure Java2D code一文)。
顯示了在不同look and feels下的滑標,從左到右,這些 LAF為:Windows (core), Metal (core), Motif (core), Liquid (third party), 和 Napkin (third party).如你所見,這個新組件提供了和當前所設置的LAF一致的外觀。
現在要何去何從?閱讀Swing核心組件的代碼。下載并學習開源組件的源代碼(像SwingX 或Flamingo),然后開始構造你自己夢想中組件吧!
【編輯推薦】