理解 Flex 4 组件生命周期(1)

自定义 Flex 组件,很重要的是要理解 Flex 4 组件的生命周期。不管你的组件是简单还是复杂,有一样是永远不变的,那就是:你的组件应该正确地实例化,正确地创建子组件,并且能够正确地显示出来;当它所展现的数据发生改变时,应该能够正确地重绘自身;或许,当它发生改变时,它应该要通知另外的组件。这一系列动作都是 Flex 框架的一系列方法定义好的。而执行这一系列方法的过程,也就是这个组件的生命周期。

下图给出一个 Flex 4 组件完整的生命周期的函数调用示意:

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()

Leave a Reply