自定义 Flex 组件,很重要的是要理解 Flex 4 组件的生命周期。不管你的组件是简单还是复杂,有一样是永远不变的,那就是:你的组件应该正确地实例化,正确地创建子组件,并且能够正确地显示出来;当它所展现的数据发生改变时,应该能够正确地重绘自身;或许,当它发生改变时,它应该要通知另外的组件。这一系列动作都是 Flex 框架的一系列方法定义好的。而执行这一系列方法的过程,也就是这个组件的生命周期。
下图给出一个 Flex 4 组件完整的生命周期的函数调用示意:
在一个组件的生命周期中很多函数会被调用,很多事件被派发,属性值也会被修改,这期间,有些函数仅调用一次,有些函数可以调用多次。如图所示,Constructor,也就是构造函数,和createChildren()
函数仅会被调用一次,而commitProperties()
,measure()
和updateDisplayList()
则会调用多次。所有这些函数(除了构造函数)都是protected
的。所以,外部代码不能直接调用这些函数,但是子类却可以轻松改写。
如果你需要重写这些函数,那么,以下两点是很重要的:
- 一定要记得调用父类的相应函数实现;
- 重写时必须添加
override
关键字,以示你是要重写函数,而不是添加一个新的函数。
下面的代码给出如何重写updateDisplayList()
函数:
// 添加 override 关键字 override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { // 使用 super 调用父类版本 super.updateDisplayList(unscaledWidth, unscaledHeight); // 你需要添加的代码 myTextDisplay.setActualSize(); // ... }
正如你所见到的一样,为添加某些特定的功能重写父类的函数实现是很简单的。但是,如果你不需要添加任何功能,不要重写这些函数!例如下面的代码:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth,unscaledHeight); }
虽然这并没有什么影响,但是这样的代码并不会给你的组件带来任何好处,并且会打乱组件的逻辑,让其他人去阅读你的代码的时候感到云里雾里。
现在,我们已经了解重写这些函数所需的一些基本知识,下面,我们来深入探讨一下这些函数的作用。只有明白这些函数是干什么的,你才能知道在重写组件的时候,究竟该修改哪个函数。
显然,最开始的部分应该是构造函数。虽然构造函数不是组件所特有的部分,但是,作为一个组件的构造函数,它与普通的 ActionScript 的类有有所区别。所以,我们还是先从构造函数开始。
构造函数
组件的构造函数与普通类的构造函数的一个很大的不同是,组件的构造函数不能带有参数。这样做有几个好处:第一,你的组件可以在 MXML 中使用;第二,如果别人使用接口或者父类(例如UIComponent
)去实例化你的组件,这就没有合适的地方去设置参数。虽然你可以给构造函数添加默认参数,但这不是一种好的实现。这样做只会引起别人的误解。使用公用属性或者访问函数反而更简单易读。
private var _property1:Number; private var _property2:Number; // 这样的属性赋值是不建议的 public function MyComponent(property1:Number=100, property2:Number=100) { _property1 = property1; _property2 = property2; } // 正确的方式 public function MyComponent() { _property1 = 100; _property2 = 100; }
如上面的代码所示,第二种方式才是推荐使用的。你应该在构造函数里面给所有的属性赋上一个初始值。现在你应该有一个疑问:我的属性值是运行时动态计算的,没有一个这样的初始值可用。这种需求是很常见的,Flex 的解决方案是commitProperties()
函数。我们在后面去详细讨论这个问题。现在,你要记住的是,构造函数里面给属性赋上一个初始值。
当你赋值完成之后,Flex 会派发一个preinitialize
事件,表示构造函数已经运行完毕。也就是说,在这个事件中,你获取的属性值都是默认值。事实上,Flex 此时在后台已经完成所有 style 值的计算,并且把该组件的parent
属性设置为实际父组件。例如,如果你把组件直接放在 MXML 中,这个组件的parent
就是Application
;如果放置在Panel
中,parent
就会是这个Panel
。
现在你已经知道,属性的默认值赋值应该放在构造函数中,但有些属性的赋值不应该放在这里:所有需要被添加到DisplayList
的属性(也就是可视子元素)的初始化都不应该放在构造函数中,而是有它们自己的位置——createChildren()
。