flex_探究Flex组件生命周期;
2012-04-27 15:02
288 查看
特别说明=>本文转载自以下网址:
http://www.ibm.com/developerworks/cn/web/1011_simq_flexlifecycle/index.html?ca=drs-
简介:最为新一代RIA技术的典型框架,AdobeFlex既有传统桌面程序的交互相应性强,健壮性以及容易编程调试的特点,又有着Web程序容易部署,更丰富多彩的UI,灵活的分布式应用等优势。而FlexFramework提供的丰富的组件库,以及健壮、规范的组件开发流程,更使得她成为了展现层技术的首选。其中要想正确高效地开发Flex组件,对组件生命周期的理解必不可少。掌握组件的生命周期,可以方便UI展示、优化组件执行性能,避免内存泄露。本文将从开发人员的角度,向您介绍Flex
组件的生命周期中的重要阶段,以及每个阶段在编码过程中应该注意的问题。学习的目的是为了最终指导实践,所以本文还会通过一个具体的例子,讲解生命周期现实应用。也许之前您一直在开发Flex组件,但是学习本文之后,您会做的更好。
要求:学习本文,您需要有一定的Flex编程经验,对Actionscript语言有一定的了解。
前言:在开始本篇文章之前,首先问大家两个问题:第一、什么是Flex;第二、为什么选择Flex。作为一个有着Flex开发经历的编程人员,您可能会给出很多答案:Flex是支持RIA技术的开发框架;Flex是一个开发和部署RIA的平台;Flex是Adobe公司的开发工具FlexBuilder等等,其实这些解释都不错,因为Flex本来就是对这一系列开发技术产品的概括词。那么为什么选择Flex呢?您肯定会给出很多Flex的优点,比如说:开发方便,因为它提供了大量的组件库可以使用;跨平台和浏览器,因为它运行在
Flashplayer播放器中之中;健壮性:Flex2及其以后的版本都基于ActionScript3.0,作为FlashPlayer9的衍生产品,实现了从解释到编译语言的飞跃,具有更高的性能和安全性。不错,这些印象都很对,因为Adobe公司对Flex设计的初衷就是,提供一个透明的平台,使得每一个基于flashplayer的开发人员(比如说Flash开发人员)都可以开发应用程序;又使得每一个应用程序的开发人员(比如说熟悉java或者.NET的程序员)都可以像开发普通应用那样地开发
RIA应用。
Flex本质:提起Flex,我们不得不追述其发展历史以及两个很重要的名词或者说技术,那就是Flash和FlashPlayer。Flash是Adobe推出的基于时间轴的交互式矢量图和Web动画的标准。一般被大量应用于互联网网页的矢量动画设计。而FlashPlayer,则是运行Flash程序的播放器,特别是flash
player9之后,他通过ActionScript3.0和新一代的虚拟机AVM2带来了更加强大的运行时功能。下面我们就来简单类比和对比下这Flex、Flash以及FlashPlayer三者之间的关系,从而得出Flex的本质到底是什么。
Flex和Flash程序有很多相同点:他们都基于ActionScript语言;并且都会被编译成一个.swf文件。FlashPlayer是运行Flash和Flex应用的播放器或者说运行时环境。他通过读取swf文件里的二进制内容同Flex或者Flash应用进行交互,从而来指示他们执行某些操作,比如说加载图片,绘制图形,发送HTTP请求等等。FlashPlayer提供了所有可以执行的操作的API,而Flash和Flex应用只能够做FlashPlayer
允许他们做的事情。所以Flashplayer环境是Flex和Flash应用开发的基础,Flex的很多类都是FlashplayerAPI的子类。
Flex和Flash应用都运行在相同的FlashPlayer里,具有着同样的用户体验。因为无论哪种应用程序,他们都只包含指令,需要由FlashPlayer来执行这些指令。所以,Flash和Flex程序的区别并不是内容不同,而是他们创建内容的方式不同。
Flex基于帧:基于以上阐述,我们知道了Flex程序的本质就是Flash,他是面相程序员的Flash变种。Flex程序的根mx.managers.SystemManager就是Flash类flash.display.MovieClip的扩展。所以,Flex也可以说成是基于时间轴的,只不过他在时间轴上的帧只有两帧:第一帧是预加载,由Flex框架控制,也就是我们在
Application运行之初看到的进度条,被称之为Preloader(如图1所示);第二帧,就是我们真正的应用程序。了解这点对于我们理解flex组件的生命周期至关重要,因为帧的执行模式有一个重要的特点,那就是在一帧中会先执行脚本后渲染屏幕,即通常称为的ElasticRacetrack模型(如图2所示)。这个模型预示着过长的代码执行会影响屏幕渲染的用户体验。Flex组件构建在帧模型基础上的,需要同样遵行帧的这种执行模式。
图1.Flex程序第一帧——Preloade
图2.[b]ElasticRacetrack帧加载模型
[/b]
Flex组件生命概述:书归正传,下面我们就来介绍Flex的生命周期。首先,Flex组建的生命周期是什么?它是指Flex框架如何同每一个Flex组件进行交互,通过什么方法来控制Flex组件的产生、刷新和销毁,以及各个组件又是如何和外界进行通讯的。概括地说,Flex组件的生命周期可以总结为两个模式、三个时期、四个方法、五个事件、七个阶段。同客观世界的物质一样,Flex
组件也有着从出生到销毁的三个时期:初生阶段、生长阶段、销毁阶段。每个阶段又会经历若干个重要步骤包括:构造、准备、装配、验证、提交、交互、拆卸和回收。其中,有些步骤组件在一生中只会经历一次,有的则会伴随生命周期重复若干。这些步骤会通过四个重要方法负责实现。那么Flex组件如何通知FlexEngineer当前处于哪个步骤,又如何告诉开发人员当前的状态如何呢?他会通过派发五个不同的事件来进行内外交互;并通过验证-提交模式(invitation-validation
pattern)来响应更新,从而实现了一个延迟渲染的松耦合的事件模型。为了能够更加容易理解以下的讲解,让我们来举例说明,我们来从Flex组件的基类UIComponent出发创建一个简单的图片查看器ImageViewer(如下图3所示)
图3、一个简单的Flex组件ImageViewer
一、出生日期
[b]组件的出生阶段包括三个步骤:构造,配置,装配和初始化。[/b](分别对应了4个protected方法:(1)constructor、createChildren方法;(2)commitProperties方法;(3)measure方法;(4)updatedisplayList方法);
构造函数(Constructor):
首先组件从构造函数(construct)开始出生。当您使用new操作符(如清单1所示)或者在mxml里声明一个组件的时候,构造函数就被调用了。通过调用构造函数,一个组件类的实例被创建在了内存里。但是仅此而已。因为他是组件生命周期的最早部分,此时组件中的子元素还没有被创建,属性值也不明,所以,通常我们在这一阶段里所做的事情很少;但是她也是一个为组件添加事件监听器的好地方,因为一个组件的构造函数会且只会被调用一次。
编程建议一:通常对组件本身(并不是组件子元素)的事件监听器会在构造函数里注册。我们通常也不会在构造函数里添加子元素,在下面的篇章里会讲解我们为什么不这样做。
清单1.构造函数被调用
privatevarmyImageView:ImageViewer=newImageViewer();
配置阶段(get&set):
初生阶段的第二个步骤是配置。在组件中定义的一些特性(properties)会在这个阶段通过set方法被赋值。但是请注意,此时组件的子元素仍然没有被创建,所以如果组件的某个属性的set方法里涉及到了对子元素或者其属性的操作的话,请格外留意。如清单2所示,假设我们把
清单2.配置阶段的执行顺序
输出顺序:
所以Adobe建议开发人员在配置阶段只缓存特性值,而把业务逻辑推迟到以后的验证阶段(参见清单3)。这就是之前提到的验证-提交模式(invitation-validationpattern),关于这一概念我们会在下面的章节做详细说明。
清单3.例子ImageViewer的setimageHeight()方法
编程建议二:使用组件的set方法是缓存特性值。将真正的业务逻辑推迟到提交阶段。
装配阶段(addChild):
组件被构造以及赋值之后,并不会自动进入整个生命周期的循环,他必须经过装配时期,及组件自身装配到显示列表(DisplayList)上。组件只有通过addChild或者addChildAt方法被装配到DisplayList上,才会依次进入到以下的生命周期时期,否则得话,以后的步骤和方法都不会被调用。为此我们可以做这样一个实验。我们在组件代码里添加trace(清单4),然后再分别执行应用程序Test1.mxml(清单5)和Test2.mxml(清单6),再来
清单4.验证装配阶段ImageViewer组件的执行顺序
清单5、应用程序Test1及其输出
<应用程序Test1.mxml>
<输出>
编程建议三:推迟组件的装配(使用addChild方法将组建添加到父节点上),除非您真的需要她。
编程提示四:覆盖createChildren方法来添加子节点,此方法会且只会被执行一次。尽量把创建动态形式和数据驱动(data-driven)形式的子元素推迟到CommitProperties()方法里添加或者执行。
编程提示五:响应组件更新请求时,调用三种验证方(invalidateProperties()、invalidateSize()、invalidateDisplayList()),而不要显试调用提交方法(commitProperties(),measure()和updateDisplayList())。Flex框架会在最合适的时候自行调用提交方法。
编程提示六:通过挑选并重写合适的提交方法,您可以把复杂的逻辑处理延迟到提交阶段通过设置标志位,可以进一步优化提交阶段的更新性能。
publicclassTableextendsTableBase{
privatevar_row:Row;
publicfunctiongetrow():Row{
returnthis._row;
}
publicfunctionsetrow(r:Row):void{
_row=r;
this.invalidateDisplayList();
}
publicfunctionTable(){
super();
}
overrideprotectedfunctioncreateChildren():void{
super.createChildren();
if(!_row){_row=newRow();}
this.addChild(_row);
}
清单12.Table和Row的初始化顺序
我们也看到controlBar在该方法的末尾才被添加到DisplayList上,正如之前提到的那样,我们只在需要的时候装配他。同时,此时也是为子节点添加监听器的好地方
关于commitProperties()方法
下面我们来看一下ImageViewer组件在commitProperties()里都做了什么(如清单13所示):
清单13.ImageViewer的commitProperties()方法
清单15.使用ImageViewer的应用Sample.mxml
关于updateDisplayList()方法
简介:最为新一代RIA技术的典型框架,AdobeFlex既有传统桌面程序的交互相应性强,健壮性以及容易编程调试的特点,又有着Web程序容易部署,更丰富多彩的UI,灵活的分布式应用等优势。而FlexFramework提供的丰富的组件库,以及健壮、规范的组件开发流程,更使得她成为了展现层技术的首选。其中要想正确高效地开发Flex组件,对组件生命周期的理解必不可少。掌握组件的生命周期,可以方便UI展示、优化组件执行性能,避免内存泄露。本文将从开发人员的角度,向您介绍Flex
组件的生命周期中的重要阶段,以及每个阶段在编码过程中应该注意的问题。学习的目的是为了最终指导实践,所以本文还会通过一个具体的例子,讲解生命周期现实应用。也许之前您一直在开发Flex组件,但是学习本文之后,您会做的更好。
要求:学习本文,您需要有一定的Flex编程经验,对Actionscript语言有一定的了解。
前言:在开始本篇文章之前,首先问大家两个问题:第一、什么是Flex;第二、为什么选择Flex。作为一个有着Flex开发经历的编程人员,您可能会给出很多答案:Flex是支持RIA技术的开发框架;Flex是一个开发和部署RIA的平台;Flex是Adobe公司的开发工具FlexBuilder等等,其实这些解释都不错,因为Flex本来就是对这一系列开发技术产品的概括词。那么为什么选择Flex呢?您肯定会给出很多Flex的优点,比如说:开发方便,因为它提供了大量的组件库可以使用;跨平台和浏览器,因为它运行在
Flashplayer播放器中之中;健壮性:Flex2及其以后的版本都基于ActionScript3.0,作为FlashPlayer9的衍生产品,实现了从解释到编译语言的飞跃,具有更高的性能和安全性。不错,这些印象都很对,因为Adobe公司对Flex设计的初衷就是,提供一个透明的平台,使得每一个基于flashplayer的开发人员(比如说Flash开发人员)都可以开发应用程序;又使得每一个应用程序的开发人员(比如说熟悉java或者.NET的程序员)都可以像开发普通应用那样地开发
RIA应用。
Flex本质:提起Flex,我们不得不追述其发展历史以及两个很重要的名词或者说技术,那就是Flash和FlashPlayer。Flash是Adobe推出的基于时间轴的交互式矢量图和Web动画的标准。一般被大量应用于互联网网页的矢量动画设计。而FlashPlayer,则是运行Flash程序的播放器,特别是flash
player9之后,他通过ActionScript3.0和新一代的虚拟机AVM2带来了更加强大的运行时功能。下面我们就来简单类比和对比下这Flex、Flash以及FlashPlayer三者之间的关系,从而得出Flex的本质到底是什么。
Flex和Flash程序有很多相同点:他们都基于ActionScript语言;并且都会被编译成一个.swf文件。FlashPlayer是运行Flash和Flex应用的播放器或者说运行时环境。他通过读取swf文件里的二进制内容同Flex或者Flash应用进行交互,从而来指示他们执行某些操作,比如说加载图片,绘制图形,发送HTTP请求等等。FlashPlayer提供了所有可以执行的操作的API,而Flash和Flex应用只能够做FlashPlayer
允许他们做的事情。所以Flashplayer环境是Flex和Flash应用开发的基础,Flex的很多类都是FlashplayerAPI的子类。
Flex和Flash应用都运行在相同的FlashPlayer里,具有着同样的用户体验。因为无论哪种应用程序,他们都只包含指令,需要由FlashPlayer来执行这些指令。所以,Flash和Flex程序的区别并不是内容不同,而是他们创建内容的方式不同。
Flex基于帧:基于以上阐述,我们知道了Flex程序的本质就是Flash,他是面相程序员的Flash变种。Flex程序的根mx.managers.SystemManager就是Flash类flash.display.MovieClip的扩展。所以,Flex也可以说成是基于时间轴的,只不过他在时间轴上的帧只有两帧:第一帧是预加载,由Flex框架控制,也就是我们在
Application运行之初看到的进度条,被称之为Preloader(如图1所示);第二帧,就是我们真正的应用程序。了解这点对于我们理解flex组件的生命周期至关重要,因为帧的执行模式有一个重要的特点,那就是在一帧中会先执行脚本后渲染屏幕,即通常称为的ElasticRacetrack模型(如图2所示)。这个模型预示着过长的代码执行会影响屏幕渲染的用户体验。Flex组件构建在帧模型基础上的,需要同样遵行帧的这种执行模式。
图1.Flex程序第一帧——Preloade
图2.[b]ElasticRacetrack帧加载模型
[/b]
Flex组件生命概述:书归正传,下面我们就来介绍Flex的生命周期。首先,Flex组建的生命周期是什么?它是指Flex框架如何同每一个Flex组件进行交互,通过什么方法来控制Flex组件的产生、刷新和销毁,以及各个组件又是如何和外界进行通讯的。概括地说,Flex组件的生命周期可以总结为两个模式、三个时期、四个方法、五个事件、七个阶段。同客观世界的物质一样,Flex
组件也有着从出生到销毁的三个时期:初生阶段、生长阶段、销毁阶段。每个阶段又会经历若干个重要步骤包括:构造、准备、装配、验证、提交、交互、拆卸和回收。其中,有些步骤组件在一生中只会经历一次,有的则会伴随生命周期重复若干。这些步骤会通过四个重要方法负责实现。那么Flex组件如何通知FlexEngineer当前处于哪个步骤,又如何告诉开发人员当前的状态如何呢?他会通过派发五个不同的事件来进行内外交互;并通过验证-提交模式(invitation-validation
pattern)来响应更新,从而实现了一个延迟渲染的松耦合的事件模型。为了能够更加容易理解以下的讲解,让我们来举例说明,我们来从Flex组件的基类UIComponent出发创建一个简单的图片查看器ImageViewer(如下图3所示)
,
然后以此为例分别讲解各个时期里,Flex框架对该组建都做了什么。
图3、一个简单的Flex组件ImageViewer
一、出生日期
[b]组件的出生阶段包括三个步骤:构造,配置,装配和初始化。[/b](分别对应了4个protected方法:(1)constructor、createChildren方法;(2)commitProperties方法;(3)measure方法;(4)updatedisplayList方法);
构造函数(Constructor):
首先组件从构造函数(construct)开始出生。当您使用new操作符(如清单1所示)或者在mxml里声明一个组件的时候,构造函数就被调用了。通过调用构造函数,一个组件类的实例被创建在了内存里。但是仅此而已。因为他是组件生命周期的最早部分,此时组件中的子元素还没有被创建,属性值也不明,所以,通常我们在这一阶段里所做的事情很少;但是她也是一个为组件添加事件监听器的好地方,因为一个组件的构造函数会且只会被调用一次。
编程建议一:通常对组件本身(并不是组件子元素)的事件监听器会在构造函数里注册。我们通常也不会在构造函数里添加子元素,在下面的篇章里会讲解我们为什么不这样做。
清单1.构造函数被调用
privatevarmyImageView:ImageViewer=newImageViewer();
配置阶段(get&set):
初生阶段的第二个步骤是配置。在组件中定义的一些特性(properties)会在这个阶段通过set方法被赋值。但是请注意,此时组件的子元素仍然没有被创建,所以如果组件的某个属性的set方法里涉及到了对子元素或者其属性的操作的话,请格外留意。如清单2所示,假设我们把
ImageViewer组件放到一个Panel容器里(),此时代码的执行顺序如下:
清单2.配置阶段的执行顺序
<mx:Panelwidth="100">
<sample:ImageViewerimageHeight="150"/>
</mx:Panel>
输出顺序:
Panel:constructor
Panel.with:setter
ImageViewerCalendar:constructor
ImageViewer.imageHeight:setter
所以Adobe建议开发人员在配置阶段只缓存特性值,而把业务逻辑推迟到以后的验证阶段(参见清单3)。这就是之前提到的验证-提交模式(invitation-validationpattern),关于这一概念我们会在下面的章节做详细说明。
清单3.例子ImageViewer的setimageHeight()方法
publicfunctionsetimageHeight(value:Number):void{
if(_imageHeight!=value){//值不相同;
_imageHeight=value;
imageHeightChanged=true;
this.invalidateDisplayList()
dispatchEvent(newEvent("imageHeightChanged"));}}
编程建议二:使用组件的set方法是缓存特性值。将真正的业务逻辑推迟到提交阶段。
装配阶段(addChild):
组件被构造以及赋值之后,并不会自动进入整个生命周期的循环,他必须经过装配时期,及组件自身装配到显示列表(DisplayList)上。组件只有通过addChild或者addChildAt方法被装配到DisplayList上,才会依次进入到以下的生命周期时期,否则得话,以后的步骤和方法都不会被调用。为此我们可以做这样一个实验。我们在组件代码里添加trace(清单4),然后再分别执行应用程序Test1.mxml(清单5)和Test2.mxml(清单6),再来
对比两个的输出结果:
清单4.验证装配阶段ImageViewer组件的执行顺序
packagedw.flex.sample{
importmx.core.UIComponent;
/**图片查看器*/
publicclassImageViewerextendsUIComponent{
/*构造函数*/
publicfunctionImageViewer(){
trace("constructor");
super();
}
overrideprotectedfunctioncreateChildren():void{
trace("createChildren");
super.createChildren()
}
overrideprotectedfunctioncommitProperties():void{
trace("commitProperties");
super.createChildren()
}
overrideprotectedfunctionmeasure():void{
trace("measure");
super.createChildren()
}
overrideprotectedfunctionupdateDisplayList(w:Number,h:Number):void{
trace("updateDisplayList");
super.updateDisplayList(w,h)
}
}
}
清单5、应用程序Test1及其输出
<应用程序Test1.mxml>
<?xmlversion="1.0"encoding="utf-8"?>
<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="vertical">
<mx:Script>
<![CDATA[
importdw.flex.sample.ImageViewer;
private
varmyImageView:ImageViewer=newImageViewer();
]]>
</mx:Script>
</mx:Application>
<输出>
constructor
清单6.应用程序Test2及其输出
<应用程序Test2.mxml>
<?xmlversion="1.0"encoding="utf-8"?>
<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="vertical">
<mx:Script>
<![CDATA[
importdw.flex.sample.ImageViewer;
private
varmyImageView:ImageViewer=newImageViewer();
overrideprotectedfunctioncreateChildren():void{
super.createChildren();
addChild(myImageView);
}
]]>
</mx:Script>
</mx:Application>
<输出>
constructor
createChildren
commitProperties
measure
updateDisplayList
编程建议三:推迟组件的装配(使用addChild方法将组建添加到父节点上),除非您真的需要她。
初始化阶段(Initialization):
初始化阶段发生在装配之后,这个阶段包含了3个子阶段,派发3个事件。组件派发Preinitialize事件。这个阶段意味着组件已经被添加到了显示列表(DisplayList)上。调用protected方法createChildren()来生成子元素。在这个阶段里,您可以覆盖这个方法添加需要的子元素。派发initialize事件。这意味着组件及其所有的子元素都已经被创建、配置装备到了DisplayList上。进入第一次验证和提交阶段。Flex框架会通过layoutManager引擎来依次调用组件的三个验证方法invalidateProperties,invalidateSize()和invalidateDisplayList().以及其分别对应的三个提交方法CommitProperties(),measure()和updateDisplayList()。关于这三组方法究竟都是做什么的,我们会在以下的验证阶段详细介绍。最后配发creationComplete事件。至此,组件的初始化阶段完成,同时也意味着Flex组件的出生时期结束了。总结以上个步骤,我们用图4来标注组件出生时期的流程图。
编程提示四:覆盖createChildren方法来添加子节点,此方法会且只会被执行一次。尽量把创建动态形式和数据驱动(data-driven)形式的子元素推迟到CommitProperties()方法里添加或者执行。
图4.组件出生时前交互流程
二、生活时期
组件经过出生时期之后就进入了生活时期,这个时期意味着组件可以被看见,被操作,甚至被删除。并且作为一个成熟的个体,组件可以和外界进行交互,对更新请求进行响应。Flex组件使用Invalidation–Validation模式来响应更新请求。
验证-提交模式(Invalidation-ValidationPattern):
Flex的生命周期是建立在帧模型基础之上的,所以同样遵循着先执行脚本后渲染屏幕的规则。为了达到流畅的视觉体验,我们通常期望能够尽量减少代码执行的消耗,而给画面渲染留下充足的时间,为了实现这点Flex使用了Invalidation–Validation模式,来实现资源的有效利用。
图5.Invalidation-Validation模式
Invalidation–Validation模式即验证提交模式提供了一个低耦合的处理过程,将数据的改变(旧数据的实效)和数据的处理(对新数据的重新使用)区分开来,这样做的好处是:只在有屏幕刷新需求的时候进行相应数据操作和计算;避免了不必要的重复渲染;
以清单7为例,通过Invalidation–Validation模式,第二行的代码的结果永远不会生效,即不会也从来没有显示到屏幕上:
清单7.相同属性被多次赋值的例子
myImageView.height=50;
myImageView.height=100;
那Flex框架是如何实现这一模式的呢?让我们来看一下Flex是如何将这一模式应用到组件上的。首先我们来看一下Flex可视化组件基类UIComponent是如何处理setwidth()方法的:
清单8.UIComponent的setwidth()方法
publicfunctionsetwidth(value:Number):void{
if(explicitWidth!=value){
explicitWidth=value;
invalidateSize();
}
//其他代码省略
}
从清单8我们可以看出,UIComponent在首先会去判断属性赋值是否改变,从而避免重复赋值带来的消耗,然后调用protected方法invalidateSize()。invalidateSize()是UIComponent提供的验证方法之一,它用来校验与组件尺寸大小相关的属性,当此方法被调用后,Flex组件引擎LayoutManager会首先检查该属性的更新请求是否已经调用过invalidateSize()方法,如果调用过,说明此类型的更改请求已经记录在案,无需赘述。如果没有,则会将此次请求记录到LayoutManager校验队列上进行等待。那么对width属性值的更新什么时候生效呢?LayoutManager会在监听到下一次RENDER事件派发的时候,将组件推送到提交阶段。
在提交阶段,LayoutManager得到更新请求队列,调用commitProperties()方法使得属性width的最新值(假设记录在了_width私有变量上)得以生效。Flex就是通过这种属性值延迟生效的方法来保证每次用于渲染画面的请求都是最新的,从而避免了不必要的资源浪费。因此,在我们的例子ImageViewer里,我们也使用了类似机制。(如清单9所示),只不过这里我们调用的是invalidateProterties()方法。与invalidateSize()类似,组件的scale属性值会在提交阶段通过调用commitProperties()方法生效。
清单9.ImageViewer的setscale方法
publicfunctionsetscale(value:Number):void
{
if(_scale!=value){
_scale=value;
scaleChanged=true;
invalidateProperties();
dispatchEvent(newEvent("scaleChanged"));
}
}
验证阶段(Invalidation)
验证阶段是组件在生活时期要经历的第一步,具体地说是响应更新请求的第一步。作为invalidation-validation循环的一份子,她会在组件的生命周期中多次执行。Flex组件基类UIcomponent提供了三种验证方法:invalidateProperties()、invalidateSize()和invalidateDisplayList()来分别响应组件对值相关,布局相关和显示相关属性的更新请求。
组件会通过两种方式进入到验证阶段:第一种方式是在初始化阶段,通过父节点调用childrenCreated()方法,组件进入第一次Invalidation–validation循环。这一次,三种验证方法都会被自动调用以确保组件初始化成功。这类调用在组件的生命周期内只会经历一次;第二种方式是响应更新请求,组件的任何一方面特性发生更新请求后,都会进入到验证阶段。而此时,用户需要根据特性的不同类别来自行决定调用哪种验证方法。还是以清单9为例,通过调用invalidateProperties()方法,确保了在接下来的提交阶段,更新会对值计算生效。在这个时期,我们通常不去做太多的事情,因为此时只是验证阶段,更新并不生效。我们所要做的只是记录此次更新,并且等待进入提交阶段。
提交阶段(Validation)
通过以上的介绍,我们已经很清楚组件更新会在提交阶段实际生效。和验证阶段的随时更新随时校验不同,Flex框架为提交阶段设计了线性执行的顺序,将三个提交方法分为了三个阶段。顺序调用三个提交方法:commitProperties(),measure()和updateDisplayList()。这样做的目的和每个方法的功能有关。commitProperties()方法主要负责属性值的计算和生效,因此首先执行,因为这些自己算过的值可能用于布局,也可能用于显示。这里也可以用来移除不需要的子节点,但是您需要通过标志位来判断子节点是否存在。
编程提示五:响应组件更新请求时,调用三种验证方(invalidateProperties()、invalidateSize()、invalidateDisplayList()),而不要显试调用提交方法(commitProperties(),measure()和updateDisplayList())。Flex框架会在最合适的时候自行调用提交方法。
measure()方法用于根据组件的内容大小计算组件尺寸。由于尺寸的计算是自下而上的,所以组件子元素的大小改变都会隐试的触发此方法。
UpdatedisplayList()方法用来更新显示列表(DisplayList)的布局以及渲染。组件的布局是一个自上而下的过程,所以在这个方法里您需要对不仅仅是组件设置布局属性,而且也要对子元素进行相应的调整。这里也是使用Flash绘图API的好地方。
这里,我们所要做的就是覆盖(override)这几个方法,在正确的时间、正确的地方做正确的事。
交互阶段(Interaction)
交互严格地说是组件生命周期中的某种行为,她是促使组件更新,推动验证-提交循环的动力。Flex使用事件驱动的交互模式,来实现一种完全松耦合的体系结构。简单地说,任何组件如果想要告诉外界当前发生了什么事或者即将发什么事的话,他可以派发一个事件,那么在该类事件可及范围内的任何组件都可以通过注册该事件的监听器的方式来对此类事件进行相应处理。关于Flex在事件机制处理方面信息的由于超出了本文的范围,这里不再多讲,感兴趣的读者可以关注后续教程《探索Flex的事件机制》,或者阅读资料部分的相应文档。
一般来说,组件的交互行为有以下几种:用户同组件的交互,比如说:输入数据,缩放大小等等;通过派发和响应事件;应用(Application)或者父节点(parent)与组件的交互。比如说ItemRenderer实例和宿主对象之间的data传递(关于ItemRenderer机制和实践讲解,也会有后续教程加以探讨)。
编程提示六:通过挑选并重写合适的提交方法,您可以把复杂的逻辑处理延迟到提交阶段通过设置标志位,可以进一步优化提交阶段的更新性能。
三、销毁时期
任何事物都会有一个归宿,Flex组件也不例外。当某个组件不再需要的时候,我们需要把她销毁(这听起来有点残酷,但是我们不得不这么做)。
拆卸阶段(removeChild)
销毁组件的一种方法是通过调用组件父亲节点(parent)的removeChild()方法,该方法会将他从显示列表(DisplayList)上拆卸掉,并且检查是否存在对此组件的引用,如果没有的话,组件会被标记为“可以回收”,这预示着组件进入到了回收阶段,并且可以被AVM垃圾回收。
回收阶段(GC)
刚才我在上边提到removeChild()方法将组建拆卸掉以后,组件可以被垃圾回收。这意味着该组件的实例会被从内存中完全删除掉,并且释放资源。但是请注意,垃圾回收并不一定在此刻马上发生,AVM有着自己的垃圾回收时间。因此这个打了标签的组件需要等待回收时刻的到来。拆卸阶段并不是组件销毁的必经阶段。当某个组件被拆卸掉之后,如果该组件包含了子组件,而他们都不存在外界引用的话,所有的元素都会被标记为“可以回收”,也就是说该系统中的子组件并不需要进入到拆卸阶段,也可以在回收时刻到来的时候被AVM回收掉。那么开发人员所需要注意的就是,在这个时刻发生之前,将引用去除掉。
实际编程中的应用
到目前为止我们学习的多是Flex生命周期的理论。学习理论的最终目的就是知道编程实践,现在我们来看一下生命周期是如何在例子ImageViewer里是被贯彻执行的。
关于createChildren()方法
清单10显示了组件ImageViewer的createChildren()方法。正如大家注意的那样,在创建每一个子节点的时候,首先判断该节点是否存在是一个很好的习惯,这样可以防止子节点在某种情况下已被实例化。是的,这种情况是可能发生的。以清单11里的Table类为例,Table里的子节点row有一个set()方法,而我们从“初生阶段”里知道,装配阶段(set方法调用)要早于初始化阶段,那么请大家思考一下清单12的执行结果会是如何.
清单10.ImageViewer的createChildren()方法
overrideprotectedfunctioncreateChildren():void{
super.createChildren();
if(!this.border){createBorder();}
if(!controlBar){controlBar=newUIComponent();}
if(!zoomInButton){
zoomInButton=newButton();
zoomInButton.label="+";
zoomInButton.addEventListener(MouseEvent.CLICK,zoomInButtonClickHandler);
controlBar.addChild(zoomInButton);
}
if(!zoomOutButton){
zoomOutButton=newButton();
zoomOutButton.label="-";
zoomOutButton.addEventListener(MouseEvent.CLICK,zoomOutButtonClickHandler);
controlBar.addChild(zoomOutButton);
}
//addedcontrolBarthelasttime
addChild(controlBar);}
清单11.Table类的例子
publicclassTableextendsTableBase{
privatevar_row:Row;
publicfunctiongetrow():Row{
returnthis._row;
}
publicfunctionsetrow(r:Row):void{
_row=r;
this.invalidateDisplayList();
}
publicfunctionTable(){
super();
}
overrideprotectedfunctioncreateChildren():void{
super.createChildren();
if(!_row){_row=newRow();}
this.addChild(_row);
}
清单12.Table和Row的初始化顺序
vart:Table=newTable();
t.row=newRow();
addChild(t);
我们也看到controlBar在该方法的末尾才被添加到DisplayList上,正如之前提到的那样,我们只在需要的时候装配他。同时,此时也是为子节点添加监听器的好地方
关于commitProperties()方法
下面我们来看一下ImageViewer组件在commitProperties()里都做了什么(如清单13所示):
清单13.ImageViewer的commitProperties()方法
overrideprotectedfunctioncommitProperties():void{
super.commitProperties();
if(sourceChanged){
if(!this.image){
image=newImage();
this.addChild(image);
image.source=this._source;
}else{
image.source=this._source;
}
sourceChanged=false;
}
if(scaleChanged){
this.imageWidth=this.imageWidth*this.scale;
this.imageHeight=this.imageHeight*this.scale;
scaleChanged=false;
}
}
CommitProperties()是验证方法invalidateProperties()所对应的提交方法,也是初始化阶段会被调用的第一个提交方法。她的目的就是使得任何通过set方法提交的数值更改生效。所以您可以看到在清单9的setscale()方法里,按照invalidation-validation模式,调用invalidateProperties()方法从而将值的生效延迟到了commitProperties()里,并且为了做到“局部更新”,我们使用了标志位scaleChanged。另外,这里一个奇怪的地方,那就是为什么在commitProperties()会有添加子节点的操作呢?这也是我们要特意举例说明的一个地方。对于有些子节点,她的诞生可能是和某些属性值相关联的,也就是我们在编程提示四里提到的动态创建或者数据驱动的子节点。这些子节点,由于他们并不是随着组件的产生而产生,而是要受属性值的变化而产生或者变化,甚至在某些情况下,根本就不需要存在。所以我们应该在值的提交阶段,也就是commitProperties()方法调用的时候,当新值真正生效的时候再去创建它。
关于measure()方法
measure()方法是组件自动计算尺寸大小的地方,在例子ImageViewer的measure()方法里(如清单14所示),我们做的事很少。这是因为,我们ImageViewer被设计为:需要显式指定大小(当然这里只是为了举例方便,您可以根据需要,制作可以自动度量大小的组件)。即在应用时设置width和height值。如清单15所示
清单14.ImageViewer的measure()方法
overrideprotectedfunctionmeasure():void{
super.measure();
measuredWidth=Math.max(image.width,controlBar.getExplicitOrMeasuredWidth());
measuredHeight=image.height+controlBar.getExplicitOrMeasuredHeight();
measuredMinWidth=measuredWidth;
measuredMinHeight=measuredHeight;
}
清单15.使用ImageViewer的应用Sample.mxml
<sample:ImageViewerwidth="400"height="350"imageWidth="150"imageHeight="140"source="product.jpg"borderColor="0x000000"/>
关于updateDisplayList()方法
updateDisplayList()方法用于对组件内容进行布局以及渲染,一切屏幕上可见的内容都需要在这里被处理,所以updateDisplayList()可以说是最繁忙的一个提交方法,他所包含的实现可以非常多。清单16中,我们省略了部分代码。只留下了需要讲解的部分。
清单16.ImageViewer的updateDisplayList()方法
overrideprotectedfunctionupdateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);
//省略部分代码
zoomInButton.setActualSize(50,20);
zoomOutButton.setActualSize(50,20);
vartmpx:Number=20;
controlBar.setActualSize(tmpx,20);
controlBar.move(0,unscaledHeight-25);
zoomInButton.move(tmpx,0);
tmpx+=60;
zoomOutButton.move(tmpx,0);
if(imageHeightChanged||imageWidthChanged){
image.width=this.imageWidth;//w;
image.height=this.imageHeight;//h-20;
vartempX:Number=x+(w-imageWidth)*0.5;
vartempY:Number=y+(h-imageHeight)*0.5;
image.x=tempX;
image.y=tempY;
imageHeightChanged=false;
imageWidthChanged=false;
varg:Graphics=graphics;
g.clear();
g.beginFill(0x6F7777);
g.drawRect(tempX-1,tempY-1,imageWidth+2,imageHeight+2);
g.endFill();
}}
清单17.ImageViewer的setimageWidth()方法。
publicfunctionsetimageWidth(value:Number):void{
if(_imageWidth!=value){
_imageWidth=value;
imageWidthChanged=true;
this.invalidateDisplayList()
dispatchEvent(newEvent("imageWidthChanged"));
}
}
结束语
本文对Flex生命周期的各个阶段进行了分类和讲解,根据各个时期的特点提出了一些编程方面的提示,并且通过实例分析将这些编程提示应用到实践中,希望对您的Flex开发工作有所帮助。
相关文章推荐
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- 探究 Flex 组件的生命周期
- flex组件的生命周期
- 转载一篇Flex组件生命周期的文章
- [转]深入浅出Flex组件生命周期Part1─ 使用ActionScript3开发Spark组件Component类
- 【转】深入浅出Flex组件生命周期Part2 ─ 使用ActionScript3开发移动Spark组件的皮肤类
- Flex组件生命周期
- Flex组件生命周期
- FLEX组件的生命周期
- Flex 组件生命周期概述
- Flex组件生命周期
- [转载]Flex组件生命周期
- FLEX组件的生命周期
- 深入FLEX组件生命周期