Stack 组件

英文原文:《The Stack Components》
原文地址:http://weblogs.macromedia.com/pent/archives/2007/04/the_stack_compo.cfm
原文作者:Peter Ent

译者:Dreamer。 本文未经同意,谢绝转载。

Stack 组件

不久前我看到过一个问题:如何使Accordion控件一次显示多个子面板? 答案是使用VBox。

这个问题及其答案引起了我的兴趣,于是我就创建了一个允许同时显示多个子面板的控件。经过了几次尝试之后我创建了Stack组件:VStack 和 HStack,如下图:

2007-4-7832.JPG

显示3个子面板的VStack

2007-4-78333.jpg

中间的面板打开、其余面板关闭的VStack

2007-4-78419.jpg

显示3个子面板的HStack

2007-4-78455.jpg

中间的面板打开、其余面板关闭的HStack

除了可以同时一次显示零个、一个或多个子面板之外,VStack组件看起来很像Accordion。HStack就是水平方向的VStack。

它们可能不是最有用的控件,但是其中有一些有趣的技巧,你可以将其用在你自己的控件中。点击这里下载源代码

它是如何创建的

我创建了一个叫做StackBase的基础控件,它扩展自UIComponent。StackBase只有一个子控件:一个Box容器;此容器对于扩展自StackBase的VStack来说是VBox容器,而对于HStack来说则是HBox容器。

我的目的是尽可能地利用在Box类中已创建的布局管理和其中已有的部分功能,以使得该组件容易编写,并且容易理解和维护。

DefaultProperty

我希望Stack组件像其它的Flex navigator组件一样,比如Accordion。也就是说,我希望可以这样使用它:

<adobe:VStack …>
<mx:Canvas …>
<!– canvas 的子组件 –>
</mx:Canvas>
<mx:Canvas label=”Page 2″>
<!– canvas 的子组件 –>
</mx:Canvas>
<!– 等等–>
</adobe:VStack>

我使用了[DefaultProperty]元数据标签来告诉Flex编译器在没有定义属性的情况下应该使用哪个属性。虽然目前来说这个属性并不明显,但是那些VStack中的Canvas容器都属于StackBase类中的一个属性:contents。查看一下StackBase.as你就会明白我的意思:

[DefaultProperty("contents")]
public class StackBase extends UIComponent …

继续往下看你会发现contents属性的定义:

[ArrayElementType("mx.core.Container")]
public function set contents( value:Array ) : void …

这个contents属性是一个数组,而且对于Stack组件来说它是一个mx.core.Container类(或者其它扩展自Container的类,比如Canvas)数组。如果将不是Container的组件放入Stack中的话,你将会得到一个与使用ViewStack或Accordion时同样的错误。

创建Content

StackBase中的createChildren()方法创建了其中的Box容器,而Box容器的子组件contents不能在这个时候创建,因为这时候contents属性可能还没有被设定。在commitProperties()里创建cotents是一个更好的选择。

为了避免有些人不了解,我在这里做一下说明,一旦所有的属性被设定而且所有的子组件创建完毕的时候(或者在调用invalidateProperties()之后)就会调用commitProperties()。这个时候子控件可能还是不可见的,但是它们已经拥有了自己的属性。这就意味着cotents数组已经设定好并且可以为Box容器添加子组件了。

创建content的过程中实际上也创建几个额外的组件。Content在上面的MXML文件中并不是Box容器的直接子组件,取而代之的是创建了一个Canvas并向其中添加content和一个用来打开关闭子面板的控件——StackHeaderButton控件,这个控件也是组件的一部分。

当content创建完毕的时候你会有以下组件:

Box
Canvas
StackHeaderButton
定义的container
Canvas
StackHeaderButton
定义的container
等等

调整大小

这个组件的窍门就是适当地调整content的大小。例如,假设你有一个有三个子面板的VStack,如果所有的子面板都可见,它们需要占用多少空间?只有一个子面板可见的时候呢?

解决办法就是在减去所有StackHeaderButton占用的空间之后,所有可见的子面板平均分配空间。以刚才的例子来说,当3个子面板都可见时,每个面板大约占1/3的空间,如果关闭了一个,其余的每个面板就占用剩余空间的1/2。.当然,你也可以根据自己的需要更改这个组件。

交互

StackHeaderButton并不只是将content分割成几部分,它还控制着content的打开和关闭。当你点击StackHeaderButton的时候,相应的面板就会打开或者关闭,同时其余的面板就会调整它们的大小,由于我使用了Resize效果所以这个过程会很流畅。

我使用的算法如下:

首先计算一下content中关闭的(在代码中标记为”collapsed”的)子面板的数量。然后获得Stack控件所占的空间,减去StackHeaderButton所占用的空间之后,剩下的空间平均分配给打开的面板。

因为当有子面板关闭的时候打开的面板都要调整它们的大小,所以在上面的工作做完之后,再为每个子面板添加Resize效果。

将所有的Resize效果都放到一个Parallel效果中,这样所有面板的大小调整就会同时进行。当空间计算完毕的时候子面板就会执行Parallel效果并调整大小。

VStack vs. HStack

本来我只计划做一个纵向的控件,但是在做完之后我决定再写一个HStack控件。我修改了一下StackBase类,改用一些protected函数来设定子面板的大小和位置,而在HStack组件则覆写了这些函数:将对高度的操作改为对宽度的操作并且将对y的操作改为对x的操作。

这两个控件大部分代码都一样,其实是在StackBase类中完成了大部分工作。

Skins

StackHeaderButton的外观使用了一个skin类。一个skin就是一个类,它的唯一工作就是为组件提供外观。一开始的时候我是用一个简单的Button作为面板头部的,但是后来发现旋转Label比旋转Button的标签更容易。你可以编写自己的skin类来更改面板头部的外观。

总结

如果你发现这些控件本身并没有用,那么你可以以它们为例来创建自己的组件。我必须承认使用Box,Canvas和Resize让工作变得容易了,但是如果你想要自己编写所有的东西也未尝不可。只需要注意以下Flex 组件的生命周期。

这个组件中一些需要注意的地方是:

[DefaultProperty][ArrayElementType]元数据标签。这两个标签使得人们可以更容易使用这个组件。

Resize 和 Parallel 效果。这样可以让所有的大小调整同时发生,也可以使控件的用户体验更好。

Skins。想一下组件的外观应该是什么样的,然后将它们分别写成skin类。这会使自定义外观更容易,而且可以将组件的功能和外观分离。

嵌入字体。为了使HStack恰当地显示,StackHeaderButton上的标签在适当的时候需要旋转90度。如果不为它们嵌入字体的话它们是不可见的。考虑到速度原因,Flash Player对多数文本都使用系统字体,但是系统字体并没有描述字母的矢量路径(vector path),所以它们不能被旋转。如果使用了嵌入字体,你就可以很容易地对文本进行旋转,歪置和放缩。



本文链接: http://www.zhuoqun.net/html/y2007/487.html 转载请注明出处,谢谢。

TrackBack引用地址:http://www.zhuoqun.net/html/y2007/487.html/trackback

相关日志


Posted in Flex, 技术.

没有评论

(Required)
(Required, will not be published)
关闭
Powered by ShareThis