今天来看一个比较简单的场景的处理:
要制作一个标题栏,标题要充满标题栏,但如果标题太长则截断显示,如下图:
可能你会说,你这不就是一个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)都遵循下面这个流程:
- 在validateDisplayList中调用getScrollableRect方法计算子组件的区域。
- 根据1的计算结果,计算滚动条的大小和位置。
- 在updateDisplayList中,对其中的所有的子组件(子容器、子组件)设置其大小(通过setActualSize)。
- 如果组件的大小发生改变,最终会调用父容器的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的基本流程:
我们注意到,右边部分是真正计算用百分比宽度的子组件的宽度,而它会把计算出来的宽度与组件的最小宽度和最大宽度比较,保证组件的宽度不会超过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.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> |