在用 ActionScript 創(chuàng)建自定義組件時,必須重載 UIComponent 類的一些方法.實現(xiàn)基本的組件結(jié)構(gòu),構(gòu)造器,以及 createChildren(),commitProperties(), measure(), layoutChrome()和 updateDisplayList()方法.
基本組件結(jié)構(gòu)
下面例子展示了Flex 組件的基本結(jié)構(gòu):
自定義組件的類定義必須以public 關(guān)鍵字修飾. 盡管包含類的定義文件中可能還有其他內(nèi)部類定義,但是,該文件中有且只能有一個 public 類定義.要將所有的內(nèi)部類定義放在包定義的
關(guān)閉大括號之下的源文件的底部.
實現(xiàn)構(gòu)造函器
用ActionScript寫的UIComponent 類或其子類的子類,應(yīng)該定義public 構(gòu)造器方法.這里的構(gòu)造器有以下特點:
1.沒有返回類型.
2. 應(yīng)被聲明為public
3. 沒有參數(shù)
4. 調(diào)用super()方法以使用父類的構(gòu)造器.
每個類只能包含一個構(gòu)造器方法;ActionScript不支持重載(overloaded)的構(gòu)造方法.使用構(gòu)造器可以設(shè)置類屬性的初始值,比如,可以設(shè)置屬性和樣式的缺省值,或者初始化數(shù)據(jù)結(jié)構(gòu),比如數(shù)組. 不要在構(gòu)造器中創(chuàng)建“子顯示對象”,構(gòu)造器只應(yīng)用于設(shè)置組件的初始值。如果組件要創(chuàng)建子組件,那么可在createChildren()方法中創(chuàng)建。
實現(xiàn)createChildren()方法
在內(nèi)部創(chuàng)建其他組件或可視化對象的組件被稱為“復(fù)合組件(composite componen)” 。例如,F(xiàn)lex ComboBox 控件包含一個用于定義ComboBox 文本區(qū)的TextInput 控件和一個用于定義ComboBox 向下箭頭的Button 控件。組件實現(xiàn)createChildren()方法,在其內(nèi)部創(chuàng)建子對象(比如其他的組件) 。
應(yīng)用開發(fā)者不要直接調(diào)用createChildren()方法;當(dāng)開發(fā)者調(diào)用addChild()方法將組件添加到父組件中時,F(xiàn)lex 會自動調(diào)用createChildren()方法。注意,createChildren()沒有與之相關(guān)的失效方法,這意味著組件被添加到父組件中時不會等上一會才調(diào)用這個方法。
例如,要定義一個新的組件,這個組件包含一個 Button 控件和一個 TextArea 控件,這里的Button 控件用于控制用戶是否能向TextArea 控件中輸入信息。下面的例子創(chuàng)建了 TextArea
和 Button 控件:
注意,在這個例子中createChildren()方法調(diào)用addChild()來添加子組件。必須對每個子對象調(diào)用addChild()方法。在創(chuàng)建子對象之后,就能使用子對象的屬性來定義子對象的特性。在這個例子中,我們創(chuàng)建了Button和TextArea控件,初始化它們,然后為它們注冊事件監(jiān)聽器。當(dāng)然,也可以給子組件添加皮膚,更完整的例子參見:例子:創(chuàng)建一個復(fù)合組件.
實現(xiàn)commitProperties()
使用commitProperties()方法來協(xié)調(diào)對組件屬性的更改。絕大多數(shù)情況下,都是對影響組件如何在屏幕上顯示的屬性使用這個方法。
當(dāng)invalidateProperties()方法調(diào)用時,F(xiàn)lex 會“安排(schedules) ”一個對commitProperties()方法的調(diào)用(這里的“安排(schedules)”指的不是立即執(zhí)行) 。
commitProperties()方法在invalidateProperties()方法調(diào)用之后的下一個“渲染事件(render event)”中被執(zhí)行。
當(dāng)使用addChild()方法向容器中添加一個組件時,F(xiàn)lex 會自動調(diào)用invalidateProperties()方法。
commitProperties()方法的調(diào)用發(fā)生在measure()方法調(diào)用之前,這讓我們能夠設(shè)置measure()方法可能使用的屬性值。
定義組件屬性的典型模式就是用getter 和setter 方法來定義屬性,如下面的例子所示:
這種設(shè)計讓 setter 方法能迅速地返回,并把對新屬性值的處理留給commitProperties()方法。
改變控件中的文本(text)對齊(alignment)方式不需要改變控件的大小。但是,一旦改變了控件的大小,就要在代碼加入對invalidateSize()方法的調(diào)用,以觸發(fā)measure()方法。
使用commitProperties()方法的優(yōu)點如下:
1.能協(xié)調(diào)對多個屬性的修改,使得這些變更能夠同時生效。
例如,可能定義多個屬性來控件組件文本的顯示,比如,文本在組件內(nèi)部的對齊(alignment)屬性。Text或者alignment屬性的變化都需要Flex 去更新組件的顯示。但是,如果text和alignment 都被改變了,在屏幕更新時,你會希望 Flex 能夠一次性地執(zhí)行所有的有關(guān)大小和位置的計算。
因此,需要使用commitProperties()方法來計算所有與其它屬性相關(guān)的屬性值。通過commitProperties ()方法來協(xié)調(diào)屬性的變更,可以減少不必要的重復(fù)處理。
2. 能夠協(xié)對同一個屬性的多次修改。
這樣就不必每次更新組件的一個屬性都執(zhí)行復(fù)雜的計算。比如,用戶更改Button控件的icon屬性以更改Button控件上顯示的圖片。根據(jù)icon 的大小或百分比計算label 的位置是一個開銷較大的操作,這樣的操作應(yīng)只在必要時執(zhí)行一次。
為了避免這樣的行為,要使用commitProperties()方法去執(zhí)行計算。 當(dāng)更新顯示時Flex會調(diào)用commitProperties()方法。 這意味著不論兩次屏幕更新之間屬性曾經(jīng)變化了多少次,F(xiàn)lex 只在屏幕更新時執(zhí)行一次計算。
下面的例子顯示在 commitProperties()方法中如何處理兩個相關(guān)屬性:
實現(xiàn)measure()方法
measure()方法設(shè)置組件的缺省大小,以像素為單位,并且也可以有選擇性地設(shè)置組件其他屬性的缺省值。
當(dāng)invalidateSize()方法的調(diào)用發(fā)生后,F(xiàn)lex 會“安排”一個對measure()方法的調(diào)用。measure()方法在invalidateSize()調(diào)用之后的下一個“渲染事件(render event)”時執(zhí)行。
當(dāng)使用addChild()方法將組件添加到容器中時,F(xiàn)lex 會自動調(diào)用invalidateSize()方法。
當(dāng)為組件設(shè)置特定的高和寬后,盡管顯示地調(diào)用 invalidateSize()方法,但 Flex 不會調(diào)用measure()方法。也就是說,只有當(dāng)組件的 explicitWidth 和 explicitHeight 屬性是 NaN 時 Flex
調(diào)用measure()方法。
在下面的例子中,由于已經(jīng)顯式地設(shè)置了Button 控件的大小,F(xiàn)lex 不會調(diào)用Button.measure()方法:
<mx:Button height="10" width="10"/>
在已有組件的子類中,只有當(dāng)正在執(zhí)行的動作需要更改父類中定義的組件大小設(shè)定規(guī)則時,才會實現(xiàn)measure()。因此,要設(shè)置一個新的缺省值,或者在運行時執(zhí)行計算以確定組件大小的
規(guī)則,就要實現(xiàn)measure()方法。在measure()方法中設(shè)置以下有關(guān)組建大小的缺省:
measuredHeight,measuredWidth 以像素為單位設(shè)定組件的缺省高度和寬度。 這些屬性被設(shè)置為0,直到 measure()方法被執(zhí)行。使它們設(shè)置為0,使得組件在缺省情況下不可見。
measuredMinHeight ,measuredMinWidth 指定組件缺省的最小高度和最小寬度,以像素為單位。Flex不能將組件的大小設(shè)置為比指定的最小值還小。
measure()只設(shè)置組件的缺省大小。在 updateDisplayList()方法中,組件的父容器將其實際大小傳遞給組件,這些屬性值與缺省值不同。
組件開發(fā)者在應(yīng)用中用以下列方式也能重載組件的缺省大小:
1, 設(shè)置 explicitHeight 和 exlicitWidth 屬性。
2, 設(shè)置 width 和 height 屬性。
3, 設(shè)置 percentHeight 和 percentWidth 屬性。
例如,定義一個 Button 控件,其缺省的大小為 100 像素寬,50 像素高,并且缺省的最小值為 50 像素寬,25 像素高,如下例所示:
下面的應(yīng)用中使用了這個 button。
Button 上沒有設(shè)置任何其它有關(guān) button 大小的約束,VBox 使用 button 的缺省大小,和缺省的最小大小來計算 VBox 在運行時的大小。也可以在應(yīng)用中重載缺省的大小設(shè)置:
在這個例子中,制定 button 的寬度是 VBox 容器寬度的 50%。當(dāng)容器寬度的 50%小于 button的最小寬度時,button 使用它的最小寬度。
計算缺省大小
上例子中, 實現(xiàn) measure()方法時用靜態(tài)的值設(shè)置缺省的大小和缺省的最小大小。 一些Flex組件使用了靜態(tài)大小,比如TextArea 的靜態(tài)大小為 100 像素寬,44像素高,而不管它所包含的文本什么樣。如果文本比 TextArea 控件大,控件就顯示滾動條。(譯者注:這里的靜態(tài)應(yīng)該指的是絕對布局下的表示像素個數(shù)的高寬值,而不是百分比 ).
通常,根據(jù)組件特點或者傳遞給該組件的信息來設(shè)置它的缺省大小。比如,Button 控件的measure()檢查它的標(biāo)簽文本,補白(margin)以及字體的特性來決定組件的缺省大小。
在下面的例子中,重載了 TextArea 控件的 measure()方法,這樣它就能夠檢測傳遞給控件的文本,以及計算 TextArea 控件的缺省大小,以使它能在一行中顯示整個文本字符串:
當(dāng) text 字符串長度超過應(yīng)用的顯示區(qū)域,通過增加邏輯來增長 TextArea 控件的高度,使文本( text) 能在多行顯示。下面應(yīng)用使用了這個組件:
實現(xiàn) layoutChrome()方法
Container 類,以及 Container 類的子類,使用layoutChrome()方法來定義容器區(qū)域的邊框(border area) 。
當(dāng)invalidateDisplayList()方法調(diào)用發(fā)生時,F(xiàn)lex 將“安排”一個 layoutChrome()方法的調(diào)用。layoutChrome()方法在invalidateDisplayList()方法調(diào)用之后的下一個“渲染事件”
期間執(zhí)行。當(dāng)使用 addChild()方法將一個組件添加到容器中時,F(xiàn)lex 自動調(diào)用invalidateDisplayList()方法。通常,使用 RectangularBorder 類來定義容器區(qū)域的邊框。比如,可以創(chuàng)建一個RectangularBorder 對象,然后在重載的 createChildren()方中,將其作為一個子控件添加到組件中。
當(dāng)創(chuàng)建容器類的子類時, 可以使用 createChildren()方法去創(chuàng)建容器的 “內(nèi)容子控件”。“內(nèi)容子控件”是指在容器中顯示的子組件。用 updateDisplayList()方法來確定“內(nèi)容子控件”的位置。
使用 layoutChrome()方法通常是用于定義容器的邊框區(qū)域和確定邊框區(qū)域的位置,以及確定要在邊框區(qū)域中顯示的附加元素。例如,Panel 容器使用 layoutChrome()方法定義 panel 容器的title 區(qū)域,這個區(qū)域用來包含title文本和close按鈕。 將容器的內(nèi)容區(qū)域和容器邊框區(qū)域分開處理的主要原因是為了處理Container.autoLayout屬性被設(shè)置為false的這種情況。當(dāng)autoLayout(自動布局)屬性被設(shè)置為true的時候,只要容器子控件的大小和位置發(fā)生變化,容器及其子控件就會進行度量和布局。缺省值為 true。
當(dāng) autoLayout 屬性被設(shè)置為 false 的時候,度量和布局只在子控件被添加到容器中或者從容器中移出時執(zhí)行。 然而, Flex 在兩種情況下都執(zhí)行 layoutChrome()。 所以, 就算在autoLayout屬性被設(shè)置為 false 的情況下,容器仍然能夠更新它的邊框區(qū)域。
實現(xiàn) updateDisplayList()方法
updateDisplayList()方法按照前面被調(diào)用的方法中設(shè)定的屬性和樣式來設(shè)定子組件的大小和位置,并畫出組件所使用的皮膚和圖片元素。而組件本身的大小由組件的父容器決定。
直到組件的 updateDisplayList()方法被調(diào)用之后,組件才能在屏幕上顯示出來。當(dāng)invalidateDisplayList()方法調(diào)用發(fā)生時,F(xiàn)lex 會“安排”一個對 updateDisplayList()方法
的調(diào)用。updateDisplayList()方法在invalidateDisplayList()方法調(diào)用之后的下一個“渲染事件”發(fā)生時才會被執(zhí)行。當(dāng)使用addChild()方法將組件添加到容器中時,F(xiàn)lex會自動調(diào)用
invalidateDisplayList()方法。
updateDisplayList()方法的主要用途如下:
1,用于設(shè)置組件中元素的大小和位置,以用于組件的顯示。很多組件由一個或者多個子組件組成,或者有若干屬性用于控制組件中信息的顯示。比如,Button 控件有一個可選的 icon,并且,使用 labelPlacement 屬性可以指定按鈕上的文字在相對于icon 的什么地方顯示(左側(cè)還是右側(cè)) 。
Button.updateDisplayList()方法使用 icon 和 labelPlacement 屬性值來控制button 的顯示。
對于有子組件的容器來說,updateDisplayList()方法控制那些子組件該如何確定位置。比如,Hbox 容器的 updateDisplayList()方法在一行上按照從左到右的循序確定子組件的位置。VBox 容器的 updateDisplayList()方法在一列上按照從上到下的順序確定子組件的位置。
要在 updateDisplayList()方法中確定一個組件的大小,應(yīng)當(dāng)使用 setActualSize()方法,而不是使用與組件大小相關(guān)的屬性,諸如 width 和 height。要確定組件的位置,應(yīng)當(dāng)使用 move()方法,而不是x和y 屬性。
2,用于畫出組件所需的所有可視元素。
組件支持很多類型的可視元素,比如皮膚,樣式,和邊框。在 updateDisplayList()方法中,可以添加這些可視元素,使用 Flash 繪畫 APIs,以及對組件中這些可視化的顯示
執(zhí)行另外一些控制。
updateDisplayList()方法形式如下:
protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
屬性有以下值:
unscaledWidth 指定組件的寬度,以像素為單位,在組件的坐標(biāo)系中,不管組件的 scaleX 屬性值是多少。這個值就是由父容器所確定的組件寬度。
unscaledHeight 指定組件的高度,以像素為單位,在組件的坐標(biāo)系中。不管組件的 scaleY 屬性值是多少。
這個值就是由父容器所確定的組件高度。
縮放發(fā)生在 Flash Player 或者 AIR 中,發(fā)生時機是在 updateDisplayList()執(zhí)行之后。比如, 一個組件的 unscaledHeight 屬性是 100, 而其 scaleY 屬性是 2.0,那么它在 Flash Player 或 AIR 中出現(xiàn)的高度為 200 像素。
VBox 容器對布局機制的重載(override) :
VBox 容器按照子組件加入到容器中的先后順序?qū)⒆咏M件按照從上到下的方式進行布局。下面的例子重載了它的 updateDisplayList()方法,這個方法使 VBox 容器按照從底向上的方式進
行布局:
在這個例子中,使用 UIComponent.move()方法設(shè)置容器中每個子控件的位置。也可以用UIComponent.x 及 UIComponent.y 屬性去設(shè)置這些坐標(biāo)。區(qū)別就是,move()方法不僅改變組件的位置, 而且在調(diào)用這個方法之后立即分發(fā)了一個 move 事件, 設(shè)置x和y 屬性也更改組件的位置,但卻在下一個屏幕更新事件中分發(fā) move 事件。
下面的應(yīng)用使用了這個組件:
在組件中畫圖
每個 Flex 組件都是 Flash Sprite 類的子類,并因此而繼承了 Sprite.graphics 屬性。Sprite.graphics 屬性所指定的 Graphics 對象可以用來向組件中添加矢量繪畫(vector
drawings)。
例如,在 updateDisplayList()方法中,可以使用 Graphics 類去畫出邊框和水平線以及其他圖形元素:
基本組件結(jié)構(gòu)
下面例子展示了Flex 組件的基本結(jié)構(gòu):
1 package myComponents
2 {
3 public class MyComponent extends UIComponent
4 {
5
.
6 }
7 }
8
必需在包中定義ActionScript 自定義組件。包能夠反映自定義組件在應(yīng)用的路徑結(jié)構(gòu)中的位置. 2 {
3 public class MyComponent extends UIComponent
4 {
5

6 }
7 }
8
自定義組件的類定義必須以public 關(guān)鍵字修飾. 盡管包含類的定義文件中可能還有其他內(nèi)部類定義,但是,該文件中有且只能有一個 public 類定義.要將所有的內(nèi)部類定義放在包定義的
關(guān)閉大括號之下的源文件的底部.
實現(xiàn)構(gòu)造函器
用ActionScript寫的UIComponent 類或其子類的子類,應(yīng)該定義public 構(gòu)造器方法.這里的構(gòu)造器有以下特點:
1.沒有返回類型.
2. 應(yīng)被聲明為public
3. 沒有參數(shù)
4. 調(diào)用super()方法以使用父類的構(gòu)造器.
每個類只能包含一個構(gòu)造器方法;ActionScript不支持重載(overloaded)的構(gòu)造方法.使用構(gòu)造器可以設(shè)置類屬性的初始值,比如,可以設(shè)置屬性和樣式的缺省值,或者初始化數(shù)據(jù)結(jié)構(gòu),比如數(shù)組. 不要在構(gòu)造器中創(chuàng)建“子顯示對象”,構(gòu)造器只應(yīng)用于設(shè)置組件的初始值。如果組件要創(chuàng)建子組件,那么可在createChildren()方法中創(chuàng)建。
實現(xiàn)createChildren()方法
在內(nèi)部創(chuàng)建其他組件或可視化對象的組件被稱為“復(fù)合組件(composite componen)” 。例如,F(xiàn)lex ComboBox 控件包含一個用于定義ComboBox 文本區(qū)的TextInput 控件和一個用于定義ComboBox 向下箭頭的Button 控件。組件實現(xiàn)createChildren()方法,在其內(nèi)部創(chuàng)建子對象(比如其他的組件) 。
應(yīng)用開發(fā)者不要直接調(diào)用createChildren()方法;當(dāng)開發(fā)者調(diào)用addChild()方法將組件添加到父組件中時,F(xiàn)lex 會自動調(diào)用createChildren()方法。注意,createChildren()沒有與之相關(guān)的失效方法,這意味著組件被添加到父組件中時不會等上一會才調(diào)用這個方法。
例如,要定義一個新的組件,這個組件包含一個 Button 控件和一個 TextArea 控件,這里的Button 控件用于控制用戶是否能向TextArea 控件中輸入信息。下面的例子創(chuàng)建了 TextArea
和 Button 控件:
// Declare two variables for the component children.
private var text_mc:TextArea;
private var mode_mc:Button;
override protected function createChildren():void {
// 調(diào)用父類的 createChildren()方法.
super.createChildren();
// 在創(chuàng)建子組件之前檢查這些子組件是否已存在
// 這是個可選項,但是這樣做使得子類可以創(chuàng)建一個不同的子組件
if (!text_mc) {
text_mc = new TextArea();
text_mc.explicitWidth = 80;
text_mc.editable = false;
text_mc.addEventListener("change", handleChangeEvent);
// 將子組件添加到自定義組件中addChild(text_mc);
}
//在創(chuàng)建子組件之前檢查這些子組件是否已存在.
if (!mode_mc){
mode_mc = new Button();
mode_mc.label = "Toggle Editing";
mode_mc.addEventListener("click", handleClickEvent);
//將子組件添加到自定義組件中
addChild(mode_mc);
}
}
private var text_mc:TextArea;
private var mode_mc:Button;
override protected function createChildren():void {
// 調(diào)用父類的 createChildren()方法.
super.createChildren();
// 在創(chuàng)建子組件之前檢查這些子組件是否已存在
// 這是個可選項,但是這樣做使得子類可以創(chuàng)建一個不同的子組件
if (!text_mc) {
text_mc = new TextArea();
text_mc.explicitWidth = 80;
text_mc.editable = false;
text_mc.addEventListener("change", handleChangeEvent);
// 將子組件添加到自定義組件中addChild(text_mc);
}
//在創(chuàng)建子組件之前檢查這些子組件是否已存在.
if (!mode_mc){
mode_mc = new Button();
mode_mc.label = "Toggle Editing";
mode_mc.addEventListener("click", handleClickEvent);
//將子組件添加到自定義組件中
addChild(mode_mc);
}
}
注意,在這個例子中createChildren()方法調(diào)用addChild()來添加子組件。必須對每個子對象調(diào)用addChild()方法。在創(chuàng)建子對象之后,就能使用子對象的屬性來定義子對象的特性。在這個例子中,我們創(chuàng)建了Button和TextArea控件,初始化它們,然后為它們注冊事件監(jiān)聽器。當(dāng)然,也可以給子組件添加皮膚,更完整的例子參見:例子:創(chuàng)建一個復(fù)合組件.
實現(xiàn)commitProperties()
使用commitProperties()方法來協(xié)調(diào)對組件屬性的更改。絕大多數(shù)情況下,都是對影響組件如何在屏幕上顯示的屬性使用這個方法。
當(dāng)invalidateProperties()方法調(diào)用時,F(xiàn)lex 會“安排(schedules) ”一個對commitProperties()方法的調(diào)用(這里的“安排(schedules)”指的不是立即執(zhí)行) 。
commitProperties()方法在invalidateProperties()方法調(diào)用之后的下一個“渲染事件(render event)”中被執(zhí)行。
當(dāng)使用addChild()方法向容器中添加一個組件時,F(xiàn)lex 會自動調(diào)用invalidateProperties()方法。
commitProperties()方法的調(diào)用發(fā)生在measure()方法調(diào)用之前,這讓我們能夠設(shè)置measure()方法可能使用的屬性值。
定義組件屬性的典型模式就是用getter 和setter 方法來定義屬性,如下面的例子所示:
// 為 alignText 屬性定義個一個 private 變量。
private var _alignText:String = "right";
// 定義個一個標(biāo)志來表明_alignText 屬性是否發(fā)生了變化 private var bAlignTextChanged:Boolean = false;
// 為屬性定義 getter 和 setter 方法
public function get alignText():String {
return _alignText;
}
public function set alignText(t:String):void {
_alignText = t;
bAlignTextChanged = true;
// 在需要的時候,觸發(fā) commitProperties(), measure(),和 updateDisplayList() 方法
// 在本例的中,不需要去重新度量(remeasure)組件
invalidateProperties();
invalidateDisplayList();
}
// 實現(xiàn) commitProperties() 方法.
override protected function commitProperties():void {
super.commitProperties();
// 檢查 flag 是否帶表 alignText 屬性已經(jīng)變化。
if (bAlignTextChanged) {
// Reset flag.
bAlignTextChanged = false;
//處理 alignment 變化
……
……
}
}
正如這個例子中看到的那樣,setter方法更改了屬性,調(diào)用invalidateProperties() 和invalidateDisplayList()方法,然后返回。Setter 方法本身不執(zhí)行任何基于新屬性值的計算。private var _alignText:String = "right";
// 定義個一個標(biāo)志來表明_alignText 屬性是否發(fā)生了變化 private var bAlignTextChanged:Boolean = false;
// 為屬性定義 getter 和 setter 方法
public function get alignText():String {
return _alignText;
}
public function set alignText(t:String):void {
_alignText = t;
bAlignTextChanged = true;
// 在需要的時候,觸發(fā) commitProperties(), measure(),和 updateDisplayList() 方法
// 在本例的中,不需要去重新度量(remeasure)組件
invalidateProperties();
invalidateDisplayList();
}
// 實現(xiàn) commitProperties() 方法.
override protected function commitProperties():void {
super.commitProperties();
// 檢查 flag 是否帶表 alignText 屬性已經(jīng)變化。
if (bAlignTextChanged) {
// Reset flag.
bAlignTextChanged = false;
//處理 alignment 變化
……
……
}
}
這種設(shè)計讓 setter 方法能迅速地返回,并把對新屬性值的處理留給commitProperties()方法。
改變控件中的文本(text)對齊(alignment)方式不需要改變控件的大小。但是,一旦改變了控件的大小,就要在代碼加入對invalidateSize()方法的調(diào)用,以觸發(fā)measure()方法。
使用commitProperties()方法的優(yōu)點如下:
1.能協(xié)調(diào)對多個屬性的修改,使得這些變更能夠同時生效。
例如,可能定義多個屬性來控件組件文本的顯示,比如,文本在組件內(nèi)部的對齊(alignment)屬性。Text或者alignment屬性的變化都需要Flex 去更新組件的顯示。但是,如果text和alignment 都被改變了,在屏幕更新時,你會希望 Flex 能夠一次性地執(zhí)行所有的有關(guān)大小和位置的計算。
因此,需要使用commitProperties()方法來計算所有與其它屬性相關(guān)的屬性值。通過commitProperties ()方法來協(xié)調(diào)屬性的變更,可以減少不必要的重復(fù)處理。
2. 能夠協(xié)對同一個屬性的多次修改。
這樣就不必每次更新組件的一個屬性都執(zhí)行復(fù)雜的計算。比如,用戶更改Button控件的icon屬性以更改Button控件上顯示的圖片。根據(jù)icon 的大小或百分比計算label 的位置是一個開銷較大的操作,這樣的操作應(yīng)只在必要時執(zhí)行一次。
為了避免這樣的行為,要使用commitProperties()方法去執(zhí)行計算。 當(dāng)更新顯示時Flex會調(diào)用commitProperties()方法。 這意味著不論兩次屏幕更新之間屬性曾經(jīng)變化了多少次,F(xiàn)lex 只在屏幕更新時執(zhí)行一次計算。
下面的例子顯示在 commitProperties()方法中如何處理兩個相關(guān)屬性:
//為 text屬性定義一個 private變量.
private var _text:String = "ModalText";
private var bTextChanged:Boolean = false;
//定義 getter 方法.
public function get text():String {
return _text;
}
//定義 setter 方法以便在屬性變化時調(diào)用 invalidateProperties()
public function set text(t:String):void {
_text = t;
bTextChanged = true;
invalidateProperties();
// 改變 text 屬性導(dǎo)致控件重新計算缺省大小 invalidateSize();
invalidateDisplayList();
}
//為 alignText 屬性定義一個 private 變量。
private var _alignText:String = "right";
private var bAlignTextChanged:Boolean = false;
public function get alignText():String
{
return _alignText;
}
public function set alignText(t:String):void {
_alignText = t;
bAlignTextChanged = true;
invalidateProperties();
invalidateDisplayList();
}
// 實現(xiàn) commitProperties() 方法.
override protected function commitProperties():void {
super.commitProperties();
//檢查兩個屬性是否發(fā)生變化的標(biāo)志
if (bTextChanged && bAlignTextChanged)
{
//重置標(biāo)志
bTextChanged = false;
bAlignTextChanged = false;
//處理兩個屬性都發(fā)生變化的情況
}
//判斷是否 text 屬性發(fā)生變化
if (bTextChanged) {
// 重置屬性。
bTextChanged = false;
// 處理 text 屬性的變化。
}
// 檢查 alignText 屬性是否變化
if (bAlignTextChanged) {
//重置屬性.
bAlignTextChanged = false;
// 處理 alignment 屬性的變化.
}
}
private var _text:String = "ModalText";
private var bTextChanged:Boolean = false;
//定義 getter 方法.
public function get text():String {
return _text;
}
//定義 setter 方法以便在屬性變化時調(diào)用 invalidateProperties()
public function set text(t:String):void {
_text = t;
bTextChanged = true;
invalidateProperties();
// 改變 text 屬性導(dǎo)致控件重新計算缺省大小 invalidateSize();
invalidateDisplayList();
}
//為 alignText 屬性定義一個 private 變量。
private var _alignText:String = "right";
private var bAlignTextChanged:Boolean = false;
public function get alignText():String
{
return _alignText;
}
public function set alignText(t:String):void {
_alignText = t;
bAlignTextChanged = true;
invalidateProperties();
invalidateDisplayList();
}
// 實現(xiàn) commitProperties() 方法.
override protected function commitProperties():void {
super.commitProperties();
//檢查兩個屬性是否發(fā)生變化的標(biāo)志
if (bTextChanged && bAlignTextChanged)
{
//重置標(biāo)志
bTextChanged = false;
bAlignTextChanged = false;
//處理兩個屬性都發(fā)生變化的情況
}
//判斷是否 text 屬性發(fā)生變化
if (bTextChanged) {
// 重置屬性。
bTextChanged = false;
// 處理 text 屬性的變化。
}
// 檢查 alignText 屬性是否變化
if (bAlignTextChanged) {
//重置屬性.
bAlignTextChanged = false;
// 處理 alignment 屬性的變化.
}
}
實現(xiàn)measure()方法
measure()方法設(shè)置組件的缺省大小,以像素為單位,并且也可以有選擇性地設(shè)置組件其他屬性的缺省值。
當(dāng)invalidateSize()方法的調(diào)用發(fā)生后,F(xiàn)lex 會“安排”一個對measure()方法的調(diào)用。measure()方法在invalidateSize()調(diào)用之后的下一個“渲染事件(render event)”時執(zhí)行。
當(dāng)使用addChild()方法將組件添加到容器中時,F(xiàn)lex 會自動調(diào)用invalidateSize()方法。
當(dāng)為組件設(shè)置特定的高和寬后,盡管顯示地調(diào)用 invalidateSize()方法,但 Flex 不會調(diào)用measure()方法。也就是說,只有當(dāng)組件的 explicitWidth 和 explicitHeight 屬性是 NaN 時 Flex
調(diào)用measure()方法。
在下面的例子中,由于已經(jīng)顯式地設(shè)置了Button 控件的大小,F(xiàn)lex 不會調(diào)用Button.measure()方法:
<mx:Button height="10" width="10"/>
在已有組件的子類中,只有當(dāng)正在執(zhí)行的動作需要更改父類中定義的組件大小設(shè)定規(guī)則時,才會實現(xiàn)measure()。因此,要設(shè)置一個新的缺省值,或者在運行時執(zhí)行計算以確定組件大小的
規(guī)則,就要實現(xiàn)measure()方法。在measure()方法中設(shè)置以下有關(guān)組建大小的缺省:
measuredHeight,measuredWidth 以像素為單位設(shè)定組件的缺省高度和寬度。 這些屬性被設(shè)置為0,直到 measure()方法被執(zhí)行。使它們設(shè)置為0,使得組件在缺省情況下不可見。
measuredMinHeight ,measuredMinWidth 指定組件缺省的最小高度和最小寬度,以像素為單位。Flex不能將組件的大小設(shè)置為比指定的最小值還小。
measure()只設(shè)置組件的缺省大小。在 updateDisplayList()方法中,組件的父容器將其實際大小傳遞給組件,這些屬性值與缺省值不同。
組件開發(fā)者在應(yīng)用中用以下列方式也能重載組件的缺省大小:
1, 設(shè)置 explicitHeight 和 exlicitWidth 屬性。
2, 設(shè)置 width 和 height 屬性。
3, 設(shè)置 percentHeight 和 percentWidth 屬性。
例如,定義一個 Button 控件,其缺省的大小為 100 像素寬,50 像素高,并且缺省的最小值為 50 像素寬,25 像素高,如下例所示:
package myComponents
{
// asAdvanced/myComponents/DeleteTextArea.as
import mx.controls.Button;
public class BlueButton extends Button {
public function BlueButton() {
super();
}
override protected function measure():void {
super.measure();
measuredWidth=100;
measuredMinWidth=50;
measuredHeight=50;
measuredMinHeight=25;
}
}
}
{
// asAdvanced/myComponents/DeleteTextArea.as
import mx.controls.Button;
public class BlueButton extends Button {
public function BlueButton() {
super();
}
override protected function measure():void {
super.measure();
measuredWidth=100;
measuredMinWidth=50;
measuredHeight=50;
measuredMinHeight=25;
}
}
}
下面的應(yīng)用中使用了這個 button。
<?xml version="1.0"?>
<!-- asAdvanced/ASAdvancedMainBlueButton.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="myComponents.*" >
<mx:VBox>
<MyComp:BlueButton/>
<mx:Button/>
</mx:VBox>
</mx:Application>
<!-- asAdvanced/ASAdvancedMainBlueButton.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="myComponents.*" >
<mx:VBox>
<MyComp:BlueButton/>
<mx:Button/>
</mx:VBox>
</mx:Application>
Button 上沒有設(shè)置任何其它有關(guān) button 大小的約束,VBox 使用 button 的缺省大小,和缺省的最小大小來計算 VBox 在運行時的大小。也可以在應(yīng)用中重載缺省的大小設(shè)置:
<?xml version="1.0"?>
<!-- asAdvanced/MainBlueButtonResize.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:MyComp="myComponents.*" >
<mx:VBox>
<MyComp:BlueButton width="50%"/>
<mx:Button/>
</mx:VBox>
</mx:Application>
<!-- asAdvanced/MainBlueButtonResize.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:MyComp="myComponents.*" >
<mx:VBox>
<MyComp:BlueButton width="50%"/>
<mx:Button/>
</mx:VBox>
</mx:Application>
在這個例子中,制定 button 的寬度是 VBox 容器寬度的 50%。當(dāng)容器寬度的 50%小于 button的最小寬度時,button 使用它的最小寬度。
計算缺省大小
上例子中, 實現(xiàn) measure()方法時用靜態(tài)的值設(shè)置缺省的大小和缺省的最小大小。 一些Flex組件使用了靜態(tài)大小,比如TextArea 的靜態(tài)大小為 100 像素寬,44像素高,而不管它所包含的文本什么樣。如果文本比 TextArea 控件大,控件就顯示滾動條。(譯者注:這里的靜態(tài)應(yīng)該指的是絕對布局下的表示像素個數(shù)的高寬值,而不是百分比 ).
通常,根據(jù)組件特點或者傳遞給該組件的信息來設(shè)置它的缺省大小。比如,Button 控件的measure()檢查它的標(biāo)簽文本,補白(margin)以及字體的特性來決定組件的缺省大小。
在下面的例子中,重載了 TextArea 控件的 measure()方法,這樣它就能夠檢測傳遞給控件的文本,以及計算 TextArea 控件的缺省大小,以使它能在一行中顯示整個文本字符串:
package myComponents
{
// asAdvanced/myComponents/MyTextArea.as
import mx.controls.TextArea;
import flash.text.TextLineMetrics;
public class MyTextArea extends TextArea
{
public function MyTextArea() {
super();
}
// The default size is the size of the text plus a 10 pixel margin.
override protected function measure():void {
super.measure();
// Calculate the default size of the control based on the
// contents of the TextArea.text property.
var lineMetrics:TextLineMetrics = measureText(text); // Add a 10 pixel border area around the text.
measuredWidth = measuredMinWidth = lineMetrics.width + 10;
measuredHeight = measuredMinHeight = lineMetrics.height + 10;
}
}
}
{
// asAdvanced/myComponents/MyTextArea.as
import mx.controls.TextArea;
import flash.text.TextLineMetrics;
public class MyTextArea extends TextArea
{
public function MyTextArea() {
super();
}
// The default size is the size of the text plus a 10 pixel margin.
override protected function measure():void {
super.measure();
// Calculate the default size of the control based on the
// contents of the TextArea.text property.
var lineMetrics:TextLineMetrics = measureText(text); // Add a 10 pixel border area around the text.
measuredWidth = measuredMinWidth = lineMetrics.width + 10;
measuredHeight = measuredMinHeight = lineMetrics.height + 10;
}
}
}
當(dāng) text 字符串長度超過應(yīng)用的顯示區(qū)域,通過增加邏輯來增長 TextArea 控件的高度,使文本( text) 能在多行顯示。下面應(yīng)用使用了這個組件:
<?xml version="1.0"?>
<!-- asAdvanced/MainMyTextArea.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="myComponents.*" >
<MyComp:MyTextArea id="myTA" text="This is a long text strring that would normally cause a TextArea control to display scroll bars. But, the custom MyTextArea control calcualtes its default size based on the text size."/>
<mx:TextArea id="flexTA" text="This is a long text strring that would normally cause a TextArea control to display scroll bars. But, the custom MyTextArea control calcualtes its default size based on the text size."/>
</mx:Application>
<!-- asAdvanced/MainMyTextArea.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="myComponents.*" >
<MyComp:MyTextArea id="myTA" text="This is a long text strring that would normally cause a TextArea control to display scroll bars. But, the custom MyTextArea control calcualtes its default size based on the text size."/>
<mx:TextArea id="flexTA" text="This is a long text strring that would normally cause a TextArea control to display scroll bars. But, the custom MyTextArea control calcualtes its default size based on the text size."/>
</mx:Application>
實現(xiàn) layoutChrome()方法
Container 類,以及 Container 類的子類,使用layoutChrome()方法來定義容器區(qū)域的邊框(border area) 。
當(dāng)invalidateDisplayList()方法調(diào)用發(fā)生時,F(xiàn)lex 將“安排”一個 layoutChrome()方法的調(diào)用。layoutChrome()方法在invalidateDisplayList()方法調(diào)用之后的下一個“渲染事件”
期間執(zhí)行。當(dāng)使用 addChild()方法將一個組件添加到容器中時,F(xiàn)lex 自動調(diào)用invalidateDisplayList()方法。通常,使用 RectangularBorder 類來定義容器區(qū)域的邊框。比如,可以創(chuàng)建一個RectangularBorder 對象,然后在重載的 createChildren()方中,將其作為一個子控件添加到組件中。
當(dāng)創(chuàng)建容器類的子類時, 可以使用 createChildren()方法去創(chuàng)建容器的 “內(nèi)容子控件”。“內(nèi)容子控件”是指在容器中顯示的子組件。用 updateDisplayList()方法來確定“內(nèi)容子控件”的位置。
使用 layoutChrome()方法通常是用于定義容器的邊框區(qū)域和確定邊框區(qū)域的位置,以及確定要在邊框區(qū)域中顯示的附加元素。例如,Panel 容器使用 layoutChrome()方法定義 panel 容器的title 區(qū)域,這個區(qū)域用來包含title文本和close按鈕。 將容器的內(nèi)容區(qū)域和容器邊框區(qū)域分開處理的主要原因是為了處理Container.autoLayout屬性被設(shè)置為false的這種情況。當(dāng)autoLayout(自動布局)屬性被設(shè)置為true的時候,只要容器子控件的大小和位置發(fā)生變化,容器及其子控件就會進行度量和布局。缺省值為 true。
當(dāng) autoLayout 屬性被設(shè)置為 false 的時候,度量和布局只在子控件被添加到容器中或者從容器中移出時執(zhí)行。 然而, Flex 在兩種情況下都執(zhí)行 layoutChrome()。 所以, 就算在autoLayout屬性被設(shè)置為 false 的情況下,容器仍然能夠更新它的邊框區(qū)域。
實現(xiàn) updateDisplayList()方法
updateDisplayList()方法按照前面被調(diào)用的方法中設(shè)定的屬性和樣式來設(shè)定子組件的大小和位置,并畫出組件所使用的皮膚和圖片元素。而組件本身的大小由組件的父容器決定。
直到組件的 updateDisplayList()方法被調(diào)用之后,組件才能在屏幕上顯示出來。當(dāng)invalidateDisplayList()方法調(diào)用發(fā)生時,F(xiàn)lex 會“安排”一個對 updateDisplayList()方法
的調(diào)用。updateDisplayList()方法在invalidateDisplayList()方法調(diào)用之后的下一個“渲染事件”發(fā)生時才會被執(zhí)行。當(dāng)使用addChild()方法將組件添加到容器中時,F(xiàn)lex會自動調(diào)用
invalidateDisplayList()方法。
updateDisplayList()方法的主要用途如下:
1,用于設(shè)置組件中元素的大小和位置,以用于組件的顯示。很多組件由一個或者多個子組件組成,或者有若干屬性用于控制組件中信息的顯示。比如,Button 控件有一個可選的 icon,并且,使用 labelPlacement 屬性可以指定按鈕上的文字在相對于icon 的什么地方顯示(左側(cè)還是右側(cè)) 。
Button.updateDisplayList()方法使用 icon 和 labelPlacement 屬性值來控制button 的顯示。
對于有子組件的容器來說,updateDisplayList()方法控制那些子組件該如何確定位置。比如,Hbox 容器的 updateDisplayList()方法在一行上按照從左到右的循序確定子組件的位置。VBox 容器的 updateDisplayList()方法在一列上按照從上到下的順序確定子組件的位置。
要在 updateDisplayList()方法中確定一個組件的大小,應(yīng)當(dāng)使用 setActualSize()方法,而不是使用與組件大小相關(guān)的屬性,諸如 width 和 height。要確定組件的位置,應(yīng)當(dāng)使用 move()方法,而不是x和y 屬性。
2,用于畫出組件所需的所有可視元素。
組件支持很多類型的可視元素,比如皮膚,樣式,和邊框。在 updateDisplayList()方法中,可以添加這些可視元素,使用 Flash 繪畫 APIs,以及對組件中這些可視化的顯示
執(zhí)行另外一些控制。
updateDisplayList()方法形式如下:
protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
屬性有以下值:
unscaledWidth 指定組件的寬度,以像素為單位,在組件的坐標(biāo)系中,不管組件的 scaleX 屬性值是多少。這個值就是由父容器所確定的組件寬度。
unscaledHeight 指定組件的高度,以像素為單位,在組件的坐標(biāo)系中。不管組件的 scaleY 屬性值是多少。
這個值就是由父容器所確定的組件高度。
縮放發(fā)生在 Flash Player 或者 AIR 中,發(fā)生時機是在 updateDisplayList()執(zhí)行之后。比如, 一個組件的 unscaledHeight 屬性是 100, 而其 scaleY 屬性是 2.0,那么它在 Flash Player 或 AIR 中出現(xiàn)的高度為 200 像素。
VBox 容器對布局機制的重載(override) :
VBox 容器按照子組件加入到容器中的先后順序?qū)⒆咏M件按照從上到下的方式進行布局。下面的例子重載了它的 updateDisplayList()方法,這個方法使 VBox 容器按照從底向上的方式進
行布局:
package myComponents
{
// asAdvanced/myComponents/BottomUpVBox.as import mx.containers.VBox;
import mx.core.EdgeMetrics;
import mx.core.UIComponent;
public class BottomUpVBox extends VBox
{
public function BottomUpVBox() {
super();
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
// Get information about the container border area.
// The usable area of the container for its children is the
// container size, minus any border areas.
var vm:EdgeMetrics = viewMetricsAndPadding;
// Get the setting for the vertical gap between children.
var gap:Number = getStyle("verticalGap");
// Determine the y coordinate of the bottom of the usable area
// of the VBox.
var yOfComp:Number = unscaledHeight-vm.bottom;
// Temp variable for a container child.
var obj:UIComponent;
for (var i:int = 0; i < numChildren; i++)
{
// Get the first container child.
obj = UIComponent(getChildAt(i));
// Determine the y coordinate of the child.
yOfComp = yOfComp - obj.height;
// Set the x and y coordinate of the child.
// Note that you do not change the x coordinate.
obj.move(obj.x, yOfComp);
// Save the y coordinate of the child,
// plus the vertical gap between children.
// This is used to calculate the coordinate
// of the next child.
yOfComp = yOfComp - gap;
}
}
}
}
{
// asAdvanced/myComponents/BottomUpVBox.as import mx.containers.VBox;
import mx.core.EdgeMetrics;
import mx.core.UIComponent;
public class BottomUpVBox extends VBox
{
public function BottomUpVBox() {
super();
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
// Get information about the container border area.
// The usable area of the container for its children is the
// container size, minus any border areas.
var vm:EdgeMetrics = viewMetricsAndPadding;
// Get the setting for the vertical gap between children.
var gap:Number = getStyle("verticalGap");
// Determine the y coordinate of the bottom of the usable area
// of the VBox.
var yOfComp:Number = unscaledHeight-vm.bottom;
// Temp variable for a container child.
var obj:UIComponent;
for (var i:int = 0; i < numChildren; i++)
{
// Get the first container child.
obj = UIComponent(getChildAt(i));
// Determine the y coordinate of the child.
yOfComp = yOfComp - obj.height;
// Set the x and y coordinate of the child.
// Note that you do not change the x coordinate.
obj.move(obj.x, yOfComp);
// Save the y coordinate of the child,
// plus the vertical gap between children.
// This is used to calculate the coordinate
// of the next child.
yOfComp = yOfComp - gap;
}
}
}
}
在這個例子中,使用 UIComponent.move()方法設(shè)置容器中每個子控件的位置。也可以用UIComponent.x 及 UIComponent.y 屬性去設(shè)置這些坐標(biāo)。區(qū)別就是,move()方法不僅改變組件的位置, 而且在調(diào)用這個方法之后立即分發(fā)了一個 move 事件, 設(shè)置x和y 屬性也更改組件的位置,但卻在下一個屏幕更新事件中分發(fā) move 事件。
下面的應(yīng)用使用了這個組件:
<?xml version="1.0"?>
<!-- asAdvanced/MainBottomVBox.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="myComponents.*" >
<MyComp:BottomUpVBox>
<mx:Label text="Label 1"/>
<mx:Button label="Button 1"/>
<mx:Label text="Label 2"/>
<mx:Button label="Button 2"/>
<mx:Label text="Label 3"/>
<mx:Button label="Button 3"/>
<mx:Label text="Label 4"/>
<mx:Button label="Button 4"/>
</MyComp:BottomUpVBox>
</mx:Application>
<!-- asAdvanced/MainBottomVBox.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComp="myComponents.*" >
<MyComp:BottomUpVBox>
<mx:Label text="Label 1"/>
<mx:Button label="Button 1"/>
<mx:Label text="Label 2"/>
<mx:Button label="Button 2"/>
<mx:Label text="Label 3"/>
<mx:Button label="Button 3"/>
<mx:Label text="Label 4"/>
<mx:Button label="Button 4"/>
</MyComp:BottomUpVBox>
</mx:Application>
在組件中畫圖
每個 Flex 組件都是 Flash Sprite 類的子類,并因此而繼承了 Sprite.graphics 屬性。Sprite.graphics 屬性所指定的 Graphics 對象可以用來向組件中添加矢量繪畫(vector
drawings)。
例如,在 updateDisplayList()方法中,可以使用 Graphics 類去畫出邊框和水平線以及其他圖形元素:
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// Draw a simple border around the child components.
graphics.lineStyle(1, 0x000000, 1.0);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
}
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// Draw a simple border around the child components.
graphics.lineStyle(1, 0x000000, 1.0);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
}