詳解所有的Swing核心組件
當(dāng)提到比較awt組件和Swing組件的區(qū)別時, 首先被提到的就是Swing 是輕量級的(lightweight).確切的說其按鈕、框架和菜單都沒有使用本地化控制(native controls).所有組件包括渲染和事件處理都是靠純java控制的。這給我們提供了很多方法去創(chuàng)建真正與平臺無關(guān)的組件,而創(chuàng)建一個在所有平臺上外觀一致的自定義組件并非一件簡單的事,這篇文章將演示如何創(chuàng)建自定義組件的過程并高亮顯示重點、步驟和易犯的錯誤。
基礎(chǔ)部分
Swing architecture overview這篇文章提供了非常優(yōu)秀的Swing結(jié)構(gòu)和開發(fā)的高級概述(high-level overview)。雖然創(chuàng)建組件要遵循一些規(guī)則會略微有點麻煩,不過最終代碼會更容易理解。它遵循”不重復(fù)發(fā)明輪子”的原則。最初你會想要把所有的東西都集中到一個類里,包括擴(kuò)展API,模型處理(狀態(tài)和通知),事務(wù)處理,布局和繪制。但是按照MVC (model-view-controller)結(jié)構(gòu)將其劃分為多個類可以讓你的組件代碼更容易理解,并且從長遠(yuǎn)來說更加容易擴(kuò)展。
所有Swing核心組件的主要部分如下:
◆組件(component)類本身,他負(fù)責(zé)提供創(chuàng)建、修改和查詢組件狀態(tài)的API
◆模型接口和和模型接口的默認(rèn)實現(xiàn),它負(fù)責(zé)處理組件的業(yè)務(wù)邏輯和組件改變通知
◆UI delegate 負(fù)責(zé)處理組件布局,事件處理(鼠標(biāo)和鍵盤事件)以及組件的繪制。
本文將配圖展示創(chuàng)建一個自定義組件,類似WINDOWS Vista Explorer 中新的 view slider。這個組件按看上去很像一個滑標(biāo)嵌入一個pop-up menu。但他和常規(guī)的JSlider又有所不同,首先,它會含有關(guān)聯(lián)標(biāo)簽(labels)和圖標(biāo)(icon)的選項(control points),其次,若range是相鄰的,(如Small Icons和Medium Icons),能夠動態(tài)的修改圖標(biāo)大小,若range是非關(guān)聯(lián)的(如Tiles-Details),滑塊只能滑動到這些選項上,不能滑動到這些選項之間的位置。
組件類:UI Delegate 裝配
自定義組件的***個類就是組件本身的API,這個API足夠簡單并且委托大部分業(yè)務(wù)邏輯給模型(參考下一章),除此之外,為了設(shè)置合適的UI delegate,你需要增加一個樣板(boilerplate)(詳細(xì)介紹請參考Enhancing Swing Applications 一文),最終,你的代碼應(yīng)該是類似這樣的:
- privatestaticfinalStringuiClassID="FlexiSliderUI";
- publicvoidsetUI(FlexiSliderUIui){
- super.setUI(ui);
- }
- publicvoidupdateUI(){
- if(UIManager.get(getUIClassID())!=null){
- setUI((FlexiSliderUI)UIManager.getUI(this));
- }else{
- setUI(newBasicFlexiSliderUI());
- }
- }
- publicFlexiSliderUIgetUI(){
- return(FlexiSliderUI)ui;
- }
- publicStringgetUIClassID(){
- returnuiClassID;
- }
這里需要注意的一點是:你需要提供一個可靠的UI delegate,如果當(dāng)前安裝的look and feel 沒有提供特殊的UI delegate時,這個UI delegate將處理組件的繪制,布局和事件處理。
模型接口
這可能是這個組件最重要的接口了。它將從業(yè)務(wù)層面表現(xiàn)的你的組件功能。模型接口不要包含任何和界面繪制相關(guān)的方法(像setFont或getPreferredSize)。我們的組件將遵循LinearGradientPaint API并且定義模型為一些range序列:
- publicstaticclassRange{
- privatebooleanisDiscrete;
- privatedoubleweight;
- publicRange(booleanisDiscrete,doubleweight){
- this.isDiscrete=isDiscrete;
- this.weight=weight;
- }
- ...
- }
模型中設(shè)置和查詢range的API
- public void setRanges(Range... range);
- public int getRangeCount();
- public Range getRange(int rangeIndex);
這個模型還提供當(dāng)前值對象的get和set方法:
模型接口的***一部分為增加/移除變化監(jiān)聽器(ChangeListeners)的方法,他遵循Swing核心組件的model接口風(fēng)格(參考BoundedRangeModel);
- void addChangeListener(ChangeListener x);
- void removeChangeListener(ChangeListener x);
模型實現(xiàn)
模型的實現(xiàn)類非常簡單,參考DefaultBoundedRangeModel,變化監(jiān)聽器(ChangeListeners)使用EventListenerList來保存。當(dāng)模型值被改變時將觸發(fā)ChangeEvent:
- protectedvoidfireStateChanged(){
- ChangeEventevent=newChangeEvent(this);
- Object[]listeners=listenerList.getListenerList();
- for(inti=listeners.length-2;i>=0;i-=2){
- if(listeners[i]==ChangeListener.class){
- ((ChangeListener)listeners[i+1]).stateChanged(event);
- }
- }
- }
以上為Swing核心組件源代碼,我們從后向前檢索所有l(wèi)istener.提取出stateChanged方法實現(xiàn)來執(zhí)行。相關(guān)方法非常簡單,檢查值是否有效,并且復(fù)制slider ranges數(shù)組(之所以這樣做是為了讓那些惡意程序代碼不能直接作用于model)
【編輯推薦】