组件的生命周期就是用组件类创建组件实例过程中调用一系列方法和派发的一系列事件,掌握这些方法和事件被调用或派发的顺序,以及方法与事件之间的相互关系,我们能够在适当时,正确地重写或者覆写组件的public及protected方法,也可以在组件的事件侦听器中写出合适的代码,从而有利于解决实际项目中所遇到的各种问题,这是我们研究组件生命周期的重点。
1. 创建组件并将其添加到容器
在Flex中,可视化组件一定要放在容器中才能显示。因此,当组件还没有被加入到任何容器之前,无法确定组件的大小以及从父容器上继承的样式,所以将组件加入容器是组件生命周期中最重要的一个组成部分。
用ActionScript创建了一个Button控件,并将其加入到容器之中,如代码清单2-19所示。
代码清单2-19 将Button控件添加到Box容器之中的代码段
- //创建一个Box容器。
- var boxContainer:Box = new Box();
- //设置Box容器。
- ...
- //创建Button控件。
- var b:Button = new Button()
- // 设置Button控件。
- b.label = "Submit";
- ...
- // 将Button添加到 Box容器中。
- boxContainer.addChild(b);
下面的步骤显示了用代码创建一个Button控件,并将这个Button控件添加到Box容器中时所发生的一切。
调用了组件的构造函数,如下代码所示。
- //创建Button控件。
- var b:Button = new Button()
通过设置组件的属性对组件进行了设置,如下代码所示。
- //设置Button控件。
- b.label = "Submit";
组件的setter方法可能调用invalidateProperties()、invalidateSize()或invalidateDisplayList() 方法。这些方法的调用会有产生什么效果呢?
调用addChild()方法将该组件添加到父组件中,如下代码所示。
图2-4是容器组件addChild方法的调用序列图,我们将根据图2-4来详细说明将组件添加到父组件时主要调用的public方法和protected方法,以及该过程所派发的事件,从而使得开发者可以知道为何以及如何重载这些方法,同时也能掌握如何编写正确的事件侦听器。
- //将Button添加到Box容器中。
- boxContainer.addChild(b);
1)首先设置子组件的通用属性。被添加容器中的子组件(这是是Button组件)的document、moduleFactory、fontContext、nestLevel等通用属性将根据情况被设置。在设置完子组件的通用属性后,被添加到容器中的子组件的stylesInitialized()方法会被调用(这个方法在mx.core.UIComonent类中不做任何处理,该方法是Flex留给UIComponent子类重载的一个高级方法)。
2) 调用容器自身的invalidateSize()方法标记容器组件的尺寸已经失效。(任何组件的invalidateSize()方法都会以自己为参数调用全局唯一的组件布局管理器(layoutManager)对象的invalidateSize()方法,组件布局管理器会把作为参数传来的这个宣告组件尺寸(Size)已经失效的组件放入其内部的一个“尺寸失效组件队列”里面,等下次渲染之前集中处理。)
3)调用容器组件自身的invalidateDisplayList()方法标记容器组件显示列表已经失效。任何组件的invalidateDisplayList()方法都会以自己为参数调用全局唯一的组件布局管理器(layoutManager)对象的invalidateDisplayList()方法,组件布局管理器的invalidateDisplayList()方法会把这个宣告组件显示列表(DisplayList)已失效的组件放入“显示列表失效组件队列”里面,等下次渲染之前集中处理。
4)容器组件派发ChildExistenceChangedEvent.CHILD_ADD事件。
5)被添加到容器中的子组件(这里是Button组件)派发FlexEvent.ADD事件。
6)调用被添加到容器中的子组件(这里是Button组件)的initialize()方法来初始化组件的内部结构。
①initialize()方法中首先在组件上派发了一个FlexEvent.PREINITIALIZE事件,使开发者在组件初始化内部结构之前有机会进行某些处理。
②initialize()方法中调用组件的createChildren()方法来创建构成组件的内部对象,这是一个protected方法,所有从UIComonent派生的组件子类都必须重载createChildren()方法,用来创建构成组件的内部对象。
③initialize()方法中调用组件的childrenCreated()方法完成组件内部对象创建之后的处理,这是protected的一个高级方法,从UIComonent派生的组件子类可以重载这个方法在组件的内部对象创建完成后做某些处理。
1. 在childrenCreated()方法中调用组件的invalidateProperties()方法标记组件属性已经失效。任何组件的invalidateProperties ()方法都会以自己为参数调用全局唯一的组件布局管理器(layoutManager)对象的invalidateProperties ()方法,组件布局管理器的invalidateProperties()方法会把参数中传来的这个宣告组件属性(Properties)已失效的组件放入其内部的一个“属性失效组件队列”里面,等下一次渲染之前对所有属性失效的组件进行集中处理。在前面的“提交阶段”一节中详细地阐述了处理过程。
2. 在childrenCreated()方法中调用组件的invalidateSize()方法标记组件的尺寸已经失效。这个方法前面已经讲过,这里不再复述。
3. 在childrenCreated()方法中调用组件的invalidateDisplayList()方法标记组件的显示列表已经失效。这个方法前面已经讲过,这里不再复述。
4. initialize()方法中调用组件的initializeAccessibility()方法初始化协助残障人士交互的功能。
5. initialize()方法中调用组件的initializationComplete()方法完成组件的内部结构初始化,这个方法会导致组件派发FlexEvent.INITIALIZE事件。 FlexEvent.INITIALIZE事件事件被派发标志着组件的内部结构已经初始化完毕。对于容器组件来说,当其所有子组件都初始化完毕后,容器才会派发自己的FlexEvent.INITIALIZE事件。
至此,子组件已经被创建和初始化,但并没有确定组件的大小和位置,也就说还没有布局。从前面的内容我们可以看到:将子组件添加到父容器的过程中,子组件和它的父容器会被注册到全局唯一的布局管理器中相关失效组件队列中,换句话说,全局唯一的布局管理器中相关失效组件队列会保存已经失效了的子组件和父容器。在注册的过程中,间接调用了stage.invalidate()方法,这个方法使Flash Player在更新屏幕前能够派发Event.RENDER事件,布局管理器侦听到这一事件后就会开始应用的布局过程。这一点会在2.5.3节中详细阐述,接下来我们将继续讨论组件布局的过程。