Flex 生命周期

//创建Button控件

var b:Button = new Button();

//设置Button控件

b.label = "Submit";

//将Button添加到Box容器中

boxContainer.addChild(b);

发生了什么?

1)首先设置子组件的通用属性。被添加容器中的子组件(这里是Button组件)的document、moduleFactory、fontContext、nestLevel、doubleClickEnabled等通用属性将根据情况被设置。在设置完子组件的通用属性后,被添加到容器的子组件的stylesInitialized()方法会被调用,这个方法在mx.core.UIComponent类中不做任何处理,该方法是Flex留给UIComponent子类重载的一个高级方法。Flex能够保证组件样式在首次调用measure方法和updateDisplayList方法之前初始化所有的样式。对于大多数组件来说,这已经足够了,但是,如果极少数组件想更早地访问样式值,则可以通过重载sytleInitialized()方法来达成目的。

2)调用容器组件自身的invalidateSize()方法标记容器组件的尺寸已经失效。任何组件的invaliddateSize()方法都会以自己为参数调用全局的唯一组件布局管理器(layoutManager)对象的invalidateSize()方法,组件布局管理器的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方法,所有从UIComponent派生的组件子类都必须重载createChildren()方法,用来创建构成组件的内部对象。

③ Initialize()方法中调用组件的childrenCreated()方法完成组件内部对象创建后的处理,这是protected的一个高级方法,从UIComponent派生的组件子类可以重载这个方法在组件内部对象创建完成之后做某些处理。

n 在childrenCreated()方法中调用组件的invalidateProperties()方法标记组件属性已经失效。任何组件的invalidateProperties()方法都会以自己为参数调用全局布局管理器(layoutManager)对象的invalidateProperties()方法,组件布局管理器的invalidateProperties()方法会把参数中传来的这个宣告组件属性(Properties)已失效的组件放入其内部的一个“属性失效组件队列”里面,等下一次渲染之前对所有属性失效的组件进行集中处理。在前面的“提交阶段”一节中详细地阐述了处理过程。

n 在childrenCreated()方法中调用组件的invalidateSize()方法标记组件的尺寸已经失效。这个方法前面已经讲过,这里不再复述。

n 在childrenCreated()方法中调用组件的invalidateDisplayList()方法标记组件的显示列表已经失效。这个方法前面已经讲过,这里不再复述。

n initialize()方法中调用组件的initializeAccessibility()方法初始化协助残障人士交互的功能。

n initialize()方法中调用组件的initializationComplete()方法完成组件的内部结构初始化,这个方法会导致组件派发FlexEvent.INITIALIZE事件。FlexEvent.INITIALIZE事件被派发标志着组件内部结构已经初始化完毕。对于容器组件来说,当其所有子组件都初始化完毕后,容器才会派发自己的FlexEvent.INITIALIZE事件。

至此,子组件已经被创建和初始化,但并没有确定组件的大小和位置,也就说还没有布局。从前面的内容我们可以看到:将子组件添加到父容器的过程中,子组件和它的父容器会被注册到全局唯一的布局管理器中的相关失效组件队列中,换句话说,全局唯一的布局管理器中相关失效组件队列会保存已经失效了的子组件和父容器。在注册的的过程中,间接地调用了stage.invalidate()方法,这个方法使flash player在更新屏幕前能够派发Event.RENDER事件,布局管理器侦听到这一事件后就会开始应用的布局过程。

组件布局

在Flex应用中,人机界面的最外层组件Application是一个容器,我们在这个容器中又放置了一些其他的布局容器,布局容器里面可以放置组件也可以放置其他布局容器。因此,人机界面是由相互嵌套的容器和组件构成的,每个组件都有一个nestLevel属性来表示它在容器中的嵌套层次。在这种嵌套层次的结构中,任何一个组件的尺寸或位置发生变化都可能对其他层次的组件布局产生影响。比如:如果容器中子组件的尺寸按照父容器尺寸的百分比确定,那么一旦父容器的尺寸发生变化,子组件的尺寸也必然会随之发生变化。可以看得出,布局是一个比较耗费性能的操作,Flex不会在每个组件属性变化时都立即实行布局操作,而是将这些组件注册到全局布局管理器中对应的失效队列中,全局布局管理其中下一次Event.RENDER事件派发时集中对这些已标记失效的组件进行布局处理。

布局管理器是Flex完成布局策略的引擎,所有的布局管理器都必须遵循mx.managers.LayoutManager接口所定义的规范。布局管理器实例在全局只有一个,默认情况下,这个全局唯一的布局管理器就是mx.managers.LayoutManager类的实例。在Flex应用启动时,mx.managers.LayoutManager类的实例被创建并被所有在容器中的组件所引用,通过组件可以访问到这个布局管理器。另外,通过LayoutManager类的静态方法getInstance()也可以访问到这个布局管理器。

布局过程被划分为三个界限清晰的阶段:提交阶段(commit phase)、度量阶段(measurement phase)、布局阶段(layout phase)。每个阶段都只有在处理完所有失效的组件后才会进入到下一个阶段。在某个阶段对组件进行处理时,组件可能会产生一些应该在前面阶段中处理的请求,这些请求将被排队,等到下一次失效那些阶段时再进行处理。下面分别阐述每个阶段所完成的处理。

(1)提交阶段

提交阶段就是提交组件已变化的属性。在Flex中,组件的某个属性值的变更可能会影响到其他属性值。比如scaleX属性,该属性表示组件在水平方法的拉伸比例,如果该属性发生变化,那么组件的explicitMinWidth、explicitWidth、explicitMaxWidth、width等属性都会根据该属性重新计算。而且在更新显示之前,scaleX属性可能会被频繁的更改。针对这样的属性,为了提高性能,开发者不应在组件属性的setter方法中立即计算与之相关的其他属性值,而是调用组件的invalidateProperties()方法将该组件标记为属性失效,从而使计算相关属性值的处理延迟到布局的提交阶段。

提交阶段从布局管理器调用自己的validateProperties()方法开始,在这个方法中,布局管理器按照组件嵌套层次的“由外到内”的顺序遍历“属性失效队列”中的组件,调用每个“属性失效组件”的validateProperties()方法。也就是说,最外层的“属性失效组件”的validateProperties()方法会最先被调用,如果是Application对象的属性也失效了,那么它的validateProperties()方法会最先被调用,这个遍历顺序又称为”自顶向下“

”属性失效组件“的validateProperties()方法会调用该组件的commitProperties()方法来完成与”失效属性“相关的其他属性值的计算。commitProperties()方法是一个protected方法,组件开发者应在自定义的UIComponent子类中重载这个方法,完成与失效组件属性相关的其他属性值计算。

UIComponent类中的commitProperties()方法处理scaleX属性的过程如下代码所示,阅读这段代码有助于开发者了解自定义组件中重载commitProperties()方法应进行哪些处理。

protected function commitProperties():void

    {

        if (_scaleX != oldScaleX)

        {

            var scalingFactorX:Number = Math.abs(_scaleX / oldScaleX);

            if (!isNaN(explicitMinWidth))

                explicitMinWidth *= scalingFactorX;

            if (!isNaN(explicitWidth))

                explicitWidth *= scalingFactorX;

            if (!isNaN(explicitMaxWidth))

                explicitMaxWidth *= scalingFactorX;

            _width *= scalingFactorX;

            super.scaleX = oldScaleX = _scaleX;

        }

        ......

    }

(2)度量阶段

所谓度量(measurement)就是计算组件的默认尺寸。如果开发者没有设置任何与组件高度和宽度相关的属性,布局管理器就需要确定默认高度和宽度,否则无法完成布局。在设计人机界面时,我们经常从组件面板中拖放组件到界面设计器上。这时我们没有设定任何与组件尺寸有关的属性,但是组件能够以一个比较合适的展示出来,这就是组件度量所起的作用。

度量阶段从布局管理器调用自己的validateSize()方法开始。在这个方法中,布局管理器按照组件嵌套层次的”由内到外“的顺序遍历”尺寸失效队列“中的组件,调用每个”尺寸失效组件“的invalidateSize()方法。也就是说,越在外层的”尺寸失效组件“的invalidateSize()方法越后被调用。如果Application对象的尺寸也失效了,那么它的invalidateSize()方法越后被调用,这个遍历顺序又被称为”自底向上“。

”尺寸失效组件“的invalidateSize()方法会调用该组件的measure()方法来确定”尺寸失效组件“的默认大小。组件的measure()方法是一个protected方法,组件开发者应在自定义的UIComponent子类中重载这个方法,完成组件默认尺寸相关属性的设置和计算。在这个方法中,主要是确定和计算measuredMinWidth、measuredMinHeight、measuredWidth、measuredHeight等表示最小默认宽度、最小默认高度、默认宽度、默认高度的属性值。下面是UIComponent类中measure()方法的代码片段。而实际上在确定组件的默认尺寸时可能会考虑到皮肤、样式等很多因素,因此可能会相当复杂。

protected function measure():void

    {

        measuredMinWidth = 0;

        measuredMinHeight = 0;

        measuredWidth = 0;

        measuredHeight = 0;

}

(3)布局阶段

布局就是根据组件的最新属性、样式以及默认尺寸来更新组件显示列表中显示对象的尺寸、位置并画出组件所用的所有皮肤(skins)及图形化元素。组件的尺寸由其容器组件所确定。

布局阶段从布局管理器调用自己的validateDisplayList()方法开始,在这个方法中,布局管理器按照组件嵌套层次的”由外到内“的顺序遍历其内部”显示列表失效组件队列“中的组件,调用每个”显示列表失效组件“的validateDisplayList()方法。也就是说,越在外层的”显示列表失效组件“的validateDisplayList()方法越先调用,如果Application对象的显示列表也失效了,那么它的validateDisplayList()方法最先被调用,这个遍历顺序又称为”自顶向下“。

”显示列表失效组件“的validateDisplayList()方法会调用该组件的updateDisplayList()方法来更新组件”显示列表“中的”子显示对象“。组件的updateDisplayList()方法是一个protected方法,组件开发者应在自定义的UIComponent子类中重载这个方法,完成组件显示列表的更新处理。即确定内部对象的位置、尺寸以及画出组件使用的所有皮肤和图形化元素。

查看mx.controls.Label组件的updateDisplayList方法,阅读这段代码有助于开发者了解在自定义中重载updateDisplayList()方法应执行哪些操作。

override protected function updateDisplayList(unscaledWidth:Number,

                                                  unscaledHeight:Number):void

    {

        super.updateDisplayList(unscaledWidth, unscaledHeight);

        // textField 占据了Label除去边白(padding)的所有区域 

        var paddingLeft:Number = getStyle("paddingLeft");

        var paddingTop:Number = getStyle("paddingTop");

        var paddingRight:Number = getStyle("paddingRight");

        var paddingBottom:Number = getStyle("paddingBottom");

        

        textField.setActualSize(unscaledWidth - paddingLeft - paddingRight,

                                unscaledHeight - paddingTop - paddingBottom);

        textField.x = paddingLeft;

        textField.y = paddingTop;

        // 现在处理文本容纳不下的截取情况

        var t:String = isHTML ? explicitHTMLText : text; 

        // 确定显示整个文本需要多大的textField

        var textFieldBounds:Rectangle = measureTextFieldBounds(t);

        

        // 普通文本使用  "..."作为截断部分的显示符号,HTML文本则直接被截断,因为跳过HTML标记只截断非标记文本比较困难。

// 但是,如果普通文本和HTML文本不能完全显示,那么会自动获得一个toolTip

        if (truncateToFit)

        {

            var truncated:Boolean;

            if (isHTML)

            {

                truncated = textFieldBounds.width > textField.width;

            }

            else

            {

                // 用前面使用"...."作为截断符号的文本重设Label的文本

                textField.text = _text;

                

                // 根据TextField的尺寸确定整个文本是否需要截断,注意,实际尺寸并未发生变化,只是改变文本内容来适应实际尺寸

                truncated = textField.truncateToFit();

            }

            // 如果开发者没有显式地设置tooltip那就隐式地设置toolTip或者清除toolTip

            if (!toolTipSet)

                super.toolTip = truncated ? text : null;

        }

        

    }


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值