1. 程式人生 > >Flex容器_Box_Label自動適應容器寬度問題

Flex容器_Box_Label自動適應容器寬度問題

今天來看一個比較簡單的場景的處理: 要製作一個標題欄,標題要充滿標題欄,但如果標題太長則截斷顯示,如下圖: 可能你會說,你這不就是一個Label嗎? 但其實Label出現截斷是有條件的:文字的長度超過Label的寬度。如果標題欄的寬度是固定了(即設定了width為某個值),那上面的效果就很容易出現了。但如果標題欄的寬度是百分比呢? 我們先看看一段程式碼:
<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:HBox width="300" backgroundColor="0xc0c0c0" backgroundAlpha="1"> 
<mx:Label id="lb" width="100%" text="{ta.text}"/> 
</mx:HBox> 
<mx:Spacer height="50"/> 
<mx:TextArea id="ta" 
text="標題很長標題很長標題很長標題很長標題很長標題很長標題很長標題很長標題很長" 
width="300" 
height="200"/> 
</mx:Application> 
看到本文的場景,可能你也會這麼寫。 這段程式碼中,另外添加了一個TextArea用於輸入文字,而標題lb與之繫結。lb的寬度設為100%,可能你會期望這樣就解決了本文的場景。 竟然會出現滾動條! 為什麼設定了Label.width為100%,會出現滾動條呢? HBox佈局原理 由於滾動條是HBox的,所以我們先來研究一下HBox的機制。 所有的Flex容器(Container)都遵循下面這個流程:
  1. 在validateDisplayList中呼叫getScrollableRect方法計運算元元件的區域。
  2. 根據1的計算結果,計算滾動條的大小和位置。
  3. 在updateDisplayList中,對其中的所有的子元件(子容器、子元件)設定其大小(通過setActualSize)。
  4. 如果元件的大小發生改變,最終會呼叫父容器的validateDisplayList,轉到1.
可以看出,滾動條的出現是根據getScrollableRect計算而來,通過檢視Container原始碼,getScrollableRect是通過計算各個子元件的width/height的所佔區域而得來的。而子元件的width/height都是在容器的updateDisplayList方法中設定的。 所有Box的佈局都是通過BoxLayout類進行的,於是問題就出在這個類中。 BoxLayout 我們來看一下BoxLayout類是怎麼工作的:
// BoxLayout.updateDisplayList中的一段。 else  // HBOX   {    gap = target.getStyle("horizontalGap");    numChildrenWithOwnSpace = n;    for (i = 0; i < n; i++)    {     if (!IUIComponent(target.getChildAt(i)).includeInLayout)      numChildrenWithOwnSpace--;    }    // stretch everything as needed including heights    excessSpace = Flex.flexChildWidthsProportionally
(
    target, w - (numChildrenWithOwnSpace - 1) * gap, h);    // Ignore scrollbar sizes for child alignment purpose.    if (horizontalScrollBar != null &&     target.horizontalScrollPolicy == ScrollPolicy.AUTO)    {     h += horizontalScrollBar.minHeight;    }    if (verticalScrollBar != null &&     target.verticalScrollPolicy == ScrollPolicy.AUTO)    {     excessSpace += verticalScrollBar.minWidth;    }    left = paddingLeft + excessSpace * horizontalAlign;    for (i = 0; i < n; i++)    {     obj = target.getLayoutChildAt(i);     top = (h - obj.height) * verticalAlign + paddingTop;     obj.move(Math.floor(left), Math.floor(top));     if (obj.includeInLayout)      left += obj.width + gap;    }   }
這段程式碼先計算各子元件的大小,核心是Flex.flexChildWidthsProportionally,然後再設定各子元件的位置。 下圖是Flex.flexChildWidthsProportionally的基本流程:
我們注意到,右邊部分是真正計算用百分比寬度的子元件的寬度,而它會把計算出來的寬度與元件的最小寬度和最大寬度比較,保證元件的寬度不會超過minWidth和maxWidth。 按照這種邏輯,各個子元件最後的寬度和可能會超出容器(HBox)的寬度,導致滾動條的出現,因為minWidth是下限。 minWidth在作怪 我們回到一開始的那段程式碼,Label的寬度被設定為100%,那它的最小寬度是多少呢?因為沒有設定,那很容易想到minWidth是在measure中計算得出的。
// Label     override protected function measure():void     {         super.measure();         var t:String = isHTML ? explicitHTMLText : text;         t = getMinimumText(t);         // Determine how large the textField would need to be         // to display the entire text.         var textFieldBounds:Rectangle = measureTextFieldBounds(t);         // Add in the padding.         measuredMinWidth = measuredWidth = textFieldBounds.width +             getStyle("paddingLeft") + getStyle("paddingRight");         measuredMinHeight = measuredHeight = textFieldBounds.height +             getStyle("paddingTop") + getStyle("paddingBottom");     }
上面的原始碼清晰的顯示,Label的最小寬度的預設值是文字的寬度! 我們總結一下原因: 由於沒有設定Label.minWidth,Label的minWidth值將會是文字的寬度,如果文字很長,minWidth將很大;由於BoxLayout的機制,子元件的寬度受限於minWidth(不能小於minWidth),導致Label的width會等於minWidth(一個比較大的值),最終導致滾動條的出現。 於是解決方案就很簡單了:
<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml
layout="vertical"> 
<mx:HBox width="300" 
backgroundColor="0xc0c0c0" 
backgroundAlpha="1"> 
<mx:Label id="lb" 
width="100%" 
minWidth="1" 
text="{ta.text}"/> 
</mx:HBox> 
<mx:Spacer height="50"/> 
<mx:TextArea id="ta" 
text="標題很長標題很長標題很長標題很長標題很長標題很長標題很長標題很長標題很長" 
width="300" 
height="200"/> 
</mx:Application>