學習筆記 如何重寫Flex組件
本文和大家重點討論一下重寫Flex組件的概念,為了模塊化設計或進一步重用,需要對Flex組件進行組合。比如一個包括TEXTAREA,BUTTON的復合控件。
重寫Flex組件
一、為什么要重寫組件
1、在Flex已有組件無法滿足業務需求,或是需要更改其可視化外觀等特性時,直接進行繼承擴展。舉例,繼承BUTTON組件的長文本卻不自動換行的自定義控件。
2、為了模塊化設計或進一步重用,需要對Flex組件進行組合。比如一個包括TEXTAREA,BUTTON的復合控件。
二、重寫Flex組件需要經常換位思考的三個角色
1、作為重寫組件的使用者,有哪些需求。比如需要在MXML標簽和AS代碼中暴露哪些屬性及對應默認值、事件和事件的處理邏輯。
2、重寫組件一般需要使用Flex原有組件,要站在原有開發者的角度了解設計規則。
3、作為重寫組件的設計者,在滿足需求的情況下盡量做到可復用、低耦合易擴展等設計原則,讓新組件像Flex原組件一樣好用。
三、重寫Flex組件AS方式和MXML方式的采用規則
1、MXML文件和AS文件***都會被編譯成AS類,只不過MXML編譯速度稍慢一些
2、如果MXML和AS都能完成的一個簡單組件,MXML更容易被創建和控制布局
3、當要覆蓋組件的某些方法時只能用AS方式
4、創建一個直接繼承UIComponent的子類時只能用AS
5、創建非可視化組件比如Formatter、validator、Effect……時,只能用AS
6、要為組件添加日志功能時,只能用AS.
四、AS方式重寫Flex組件常規步驟
1、如果有必要,為組件創建所有基于標記(tag-based)的皮膚(skins)
2、創建ActionScript類文件
⑴從一個基類擴展,比如UIComponent或者其他的組件類
⑵指定使用者能夠通過MXML標記進行設置的屬性
⑶嵌入(Embed)所有的圖片和皮膚文件,文件大小盡可能小
⑷實現構造器,可以設置屬性和樣式的缺省值,或者初始化數據結構,比如數組
⑸根據需要,確定是否覆蓋以下五個方法:
(a)UIComponent.createChildren()方法,創建組件的子組件
(b)UIComponent.commitProperties()方法,提交組件所有的屬性變化,設置measure()方法可能使用的屬性值。絕大多數情況下,都是對影響組件如何在屏幕上顯示的屬性使用這個方法
(c)UIComponent.measure()方法,設置組件的缺省size(measuredWidth、measuredHeight)和缺省的最小size(measuredMinWidth、measuredMinHeight)
(d)UIComponent.layoutChrome()方法,用于定義容器的邊框區域和確定邊框區域的位置,以及確定要在邊框區域中顯示的附加元素。例如,Panel容器使用layoutChrome()方法定義panel容器的title區域,這個區域用來包含title文本和close按鈕。
通常,使用RectangularBorder類來定義容器區域的邊框,而不是用圖片資源去包圍組件。比如創建一個RectangularBorder對象,然后在重載的createChildren()方法中,將其作為一個內容子控件添加到組件中,再用updateDisplayList()方法來確定其位置。
將容器的內容區域和容器邊框區域分開處理的主要原因是為了應對Container.autoLayout
屬性被設置為false的這種情況。當autoLayout(自動布局)屬性使用默認值true時,只要容器子控件的大小和位置發生變化,容器及子控件就會進行度量和布局。而當其為false時,度量和布局只在子控件被添加或移出容器時才執行。分開處理讓Flex在這兩種情況下都執行layoutChrome(),從而在autoLayout屬性為false的情況下,容器仍能夠更新它的邊框區域。
(e)UIComponent.updateDisplayList()方法,根據以前所設置的屬性和樣式來確定組件的子組件在屏幕上的大小(size)及位置(position),并且畫出組件所使用的所有皮膚(skins)及圖形化元素。組件的父容器負責確定組件本身大小(size)。
要在updateDisplayList()方法中確定一個組件的大小,當子組件是UICOMPONENT時使用setActualSize()方法,而不是UICOMPONENT則使用與組件大小相關的屬性width和height。要確定組件的位置,當子組件是UICOMPONENT時使用move()方法,而不是UICOMPONENT則使用x和y屬性。一個區別就是move()方法不僅改變了組件位置,而且在調用這個方法之后立即分發了一個move事件,設置x和y屬性也更改組件的位置,但卻在下一個屏幕更新事件中才會分發move事件。
組件支持很多類型的可視元素,比如皮膚,樣式和邊框。在updateDisplayList()方法中,可以添加這些可視元素,并對它們進行一些控制。由于UICOMPONENT繼承自SPRITE,所以可以使用Graphics對象中的Flash繪畫APIs進行繪制圖形,比如使用Graphics類去畫邊框水平線以及其他圖形元素:
graphics.lineStyle(1,0x000000,1.0);
graphics.drawRect(0,0,unscaledWidth,unscaledHeight);
updateDisplayList()方法形式如下:
protectedfunctionupdateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void
以像素為單位的組件坐標系中,unscaledWidth和unscaledHeight是由父容器確定的組件大小,而不管組件的scaleX,scaleY是多少。縮放發生在FlashPlayer或者AIR中,發生時機是在updateDisplayList()執行之后。比如一個組件的unscaledHeight屬性是100,而其scaleY屬性是2.0,那么它在FlashPlayer或AIR中出現的高度為200像素。
需要注意的是,定義新組件時不一定要重載所有的五個方法,只需重載實現組件功能所必需的即可。比如實現一個自定義的Button控件,該控件使用新的機制來定義缺省大小(size)。在這種情況下,只需要重載measure()方法。或者,要實現VBox容器的一個新子類。新子類利用VBox類已有的所有有關設定大小(sizing)的邏輯,但是變更了VBox類的布局邏輯以實現從底部到頂部的方式來布局容器中的子控件,而不是自頂向下的布局。在這種情況下,只需要重載updateDisplayList()方法。
(6)增加屬性(properties),方法(methods),樣式(styles),事件(events)以及元數據
3、以ActionScript文件或者SWC文件的形式部署組件
五、深入Flex組件生命周期
1、生命周期(LifeCycle)簡述
⑴調用組件構造函數。構造函數沒有返回類型,沒有參數,使用super()調用父類的構造器
⑵使用set,get設置組件屬性,常在set方法內監控一個布爾變量來實現失效機制
⑶調用addChild()方法將組件添加到父組件顯示列表中,Flex將自動調用createChildren(),invalidateProperties(),invalidateSize(),invalidateDisplayList()。只有將組件添加到父容器中,Flex才能確定它的大小(size),設置它所繼承樣式(style)屬性,或者在屏幕上畫出它
⑷組件的parent屬性設置為對父容器的引用
⑸樣式(style)設置
⑹組件分發preinitialize事件
⑺調用組件createChildren()方法
⑻調用invalidateProperties(),invalidateSize(),invalidateDisplayList()失效方法,Flex將在下一個“渲染事件”(renderevent)期間對相應的commitProperties(),measure(),updateDisplayList()方法進行調用。這個規則唯一例外就是當用戶設置組件的height和width屬性時,Flex不會調用measure()方法。也就是說,只有當組件的explicitWidth和explicitHeight屬性是NaN時Flex才會調用measure()方法。
⑼組件分發initialize事件。此時組件所有的子組件初始化完成,但組件尚未更改size和布局,可以利用這個事件在組件布局之前執行一些附加的處理
⑽在父容器上分發childAdd事件
⑾在父容器上分發initialize事件
⑿在下一個“渲染事件”(renderevent)中,Flex執行以下動作:
a.調用組件的commitProperties()方法
b.調用組件的measure()方法
c.調用組件的layoutChrome()方法
d.調用組件的updateDisplayList()方法
e.在組件上分發updateComplete事件
⒀如果commitProperties(),measure(),updateDisplayList()方法調用了invalidateProperties(),invalidateSize(),invalidateDisplayList()方法,則Flexh會分發另外一個render事件
⒁在***的render事件發生后,Flex執行以下動作:
a.設置組件visible屬性使其可視
b.組件分發creationComplete事件,組件的大小(size)和布局被確定,這個事件只在組件創建時分發一次
c.組件分發updateComplete事件。無論什么時候,只要組件的布局(layout),位置,大小或其它可視的屬性發生變化就會分發這事件,然后更新組件來正確地顯示。
2、為什么使用失效機制(invalidationmechanism)
一種情況是,當設置了組件的多個屬性后,比如Button控件的label和icon屬性,我們需要所有屬性被設置后一次性執行commitProperties(),measure(),updateDisplayList()方法,而不是設置過label屬性后執行一遍這些方法,然后在設置icon屬性后又執行一次這些方法。
另一種情況是幾個組件同時更改了它們的字體大小。程序更改字體大小的執行速度大大快于Flex更新應用的速度,因此要在確定最終更改字體之后才開始更新布局。另外,Flex需要協調布局操作以消除任何冗余過程,而不是在每個組件更新它的字體大小之后都執行一次布局操作。
Flex使用“失效機制(invalidationmechanism)”來同步組件的更改。Flex用一系列方法的調用,比如在setter方法內監控一個變更變量來標記組件的某些東西已經發生變化,然后在下一個“渲染事件(renderevent)”中觸發組件的commitProperties(),measure(),layoutChrome(),updateDisplayList()檢查這些布爾變量來完成最終的變更邏輯。這樣做的額外好處就是setter方法可以更迅速地返回,把對新屬性值的處理留給了commitProperties()方法。
失效方法及其對應的觸發函數如下:
invalidateProperties()通知組件,以使下次屏幕更新時,它的commitProperties()方法被調用。
invalidateSize()通知組件,以使下次屏幕更新時,它的measure()方法被調用。
invalidateDisplayList()通知組件,以使下次屏幕更新時它的layoutChrome()方法和
updateDisplayList()方法能被調用。
當組件調用一個“失效”方法時,它就通知Flex該組件已經被更新。當多個組件調用失效
方法,Flex會在schedules中協調這些更新,以使這些更新操作在下一次屏幕更新時一起執行。注意,createChildren()沒有對應的失效方法,它會在調用后被立即執行。
【編輯推薦】
- Flex4beta的Spark布局特性解析
- Flex3到Flex4的轉變新特性值得關注
- Flex4Beta引入新元素功能更強大
- FlexBuilder3.0與Eclipse3.4的***結合
- 技術分享 Flex4兼容模式如何移植到發布版本