组件失效机制(invalidationmechanism)是Flex用来提高应用性能的一项技术,组件生命周期与布局主要利用这一机制来实现,本节对该机制进行总结并探讨该机制如何实现。
1. 什么是组件失效机制
在组件的生命周期中,应用可能会改变组件的大小和位置,改变组件的属性来控制组件的显示,或者更改组件的样式和皮肤属性。比如,可能更改组件中所显示文本(Text)的字体(font)。组件中文本字体发生变化,那么组件的尺寸也可能随之变化,这就会影响到组件的布局。从前面的内容中我们可以知道,布局操作会使Flex自动调用自定义组件的commitProperties()、measure(),layoutChrome()以及updateDisplayList()等一系列方法。
通过程序来更改“字体(font)”这个属性值的执行速度远远快于Flex渲染图形和更新屏幕的速度。因此,应该在确定最终字体之后再更新布局。
当设置了组件的多个属性后,每个属性都可能影响到组件的尺寸,比如Button控件的label和icon属性,开发者肯定希望所有属性全部设置完成后一次性地执行commitProperties()、measure()和updateDisplayList()方法,而不是在设置完label属性后执行一遍这些方法,然后在设置icon属性后又执行一遍这些方法。
另外,可能会有多个组件同时改变字体,组件的字体改变都可能会引起组件尺寸的变化,从而影响它们之间的相对位置。这时应该让Flex去协调布局操作,以消除冗余处理,而不是每个组件更新字体之后都执行一次布局操作。
Flex使用失效机制来同步组件的变更。正如前面所讲到的那样,Flex用一系列方法的调用来标记组件的某些东西已经发生变化,然后将其延迟到下一次屏幕更新时通过布局管理器统一调用组件的commitProperties()、measure()、layoutChrome()以及updateDisplayList()方法。
表2-1列出了组件中有关“失效(invalidation)”的方法。
表2-1 组件中“失效”方法列表
2. Flex如何实现“失效机制”
Flex通过UIComponent的callLater()方法实现组件的“失效机制”。callLater()方法声明如下:
public function callLater(method:Function, args:Array =null):void
callLater是一个非常重要的底层方法,callLater方法将给定的method及其参数args放入内部队列中,当下一次屏幕更新时调用内部队列中的方法。那么“下一次屏幕更新”指的是什么呢?通过研究callLater方法的源代码我们发现callLater方法主要做三件事情:
首先将需要延迟调用的method方法及其参数args放入组件的内部队列中。
为舞台(Stage)对象的ENTER_FRAME和RENDER事件设置侦听器,在这两个事件的侦听器中会调用在“内部队列”中延迟的方法。队列中的方法一旦被调用就会从队列中清除。
调用舞台(stage)对象的invalidate()方法,这样FlashPlayer在显示列表渲染前能够派发RENDER事件。
<!-- CallLater.mxml -->
<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"
enterFrame="this.onEnterFrame(event)">
<mx:Script><![CDATA[
[Bindable]
public var text:String = "这是一串通过callLater方法实现滚动文字";
[Bindable]
//文字移动速度。
public var speed:Number = 5;
import flash.display.Stage
public function initTicker():void
{
//从右侧开始移动文字
theText.move( this.width+10, 0 );
callLater(moveText);
}
private function onEnterFrame(event:Event):void
{
trace("----------------进入新一帧------------------------");
}
public function moveText():void
{
var xpos:Number = theText.x;
if( xpos-speed+theText.width < 0 )
{
xpos = this.width+10; //从右侧开始移动文字
}
xpos -= speed;
//每帧应当执行两次该代码,分别在进入帧和渲染显示列表前各执行一次。
trace(speed, xpos);
//该代码的调用将导致显示列表失效,从而RENDER事件被Flash派发。
theText.move(xpos,0);
// 如果下面代码在"失效动作"阶段执行,则moveText方法将
在进入新一帧时被调用。如果该代码在进入新一帧时被调用,也
就是在"用户动作"阶段被调用,则moveText方法将在"失效动作"阶
段执行,也就是在渲染显示列表前执行。
callLater(moveText);
}
public function changeSpeed():void
{
speed = speedSelector.value;
}
]]>
</mx:Script>
<mx:Panel title="步进器例子" width="400"height="200">
<mx:Canvas creationComplete="initTicker()"
horizontalScrollPolicy="off"
backgroundColor="red" color="white"width="100%">
<mx:Label id="theText" text="{text}"y="0"/>
</mx:Canvas>
<mx:HBox>
<mx:Label text="速度:"/>
<mx:HSlider minimum="1" maximum="10"value="{speed}"
id="speedSelector" snapInterval="1" tickInterval="1"
change="changeSpeed()"/>
</mx:HBox>
</mx:Panel>
</mx:Application>
通过调试我们可以发现,在执行一帧的过程中,moveText()方法被执行两次,因为trace(speed,xpos)方法输出了两次。调试状态下,控制台输入如下所示:
----------------进入新一帧------------------------
5 1025
----------------进入新一帧------------------------
5 1020
5 1015
----进入新一帧------------------------------------
5 1010
5 1005
----------------进入新一帧------------------------
......
1. 什么是组件失效机制
在组件的生命周期中,应用可能会改变组件的大小和位置,改变组件的属性来控制组件的显示,或者更改组件的样式和皮肤属性。比如,可能更改组件中所显示文本(Text)的字体(font)。组件中文本字体发生变化,那么组件的尺寸也可能随之变化,这就会影响到组件的布局。从前面的内容中我们可以知道,布局操作会使Flex自动调用自定义组件的commitProperties()、measure(),layoutChrome()以及updateDisplayList()等一系列方法。
通过程序来更改“字体(font)”这个属性值的执行速度远远快于Flex渲染图形和更新屏幕的速度。因此,应该在确定最终字体之后再更新布局。
当设置了组件的多个属性后,每个属性都可能影响到组件的尺寸,比如Button控件的label和icon属性,开发者肯定希望所有属性全部设置完成后一次性地执行commitProperties()、measure()和updateDisplayList()方法,而不是在设置完label属性后执行一遍这些方法,然后在设置icon属性后又执行一遍这些方法。
另外,可能会有多个组件同时改变字体,组件的字体改变都可能会引起组件尺寸的变化,从而影响它们之间的相对位置。这时应该让Flex去协调布局操作,以消除冗余处理,而不是每个组件更新字体之后都执行一次布局操作。
Flex使用失效机制来同步组件的变更。正如前面所讲到的那样,Flex用一系列方法的调用来标记组件的某些东西已经发生变化,然后将其延迟到下一次屏幕更新时通过布局管理器统一调用组件的commitProperties()、measure()、layoutChrome()以及updateDisplayList()方法。
表2-1列出了组件中有关“失效(invalidation)”的方法。
表2-1 组件中“失效”方法列表
2. Flex如何实现“失效机制”
Flex通过UIComponent的callLater()方法实现组件的“失效机制”。callLater()方法声明如下:
public function callLater(method:Function, args:Array =null):void
callLater是一个非常重要的底层方法,callLater方法将给定的method及其参数args放入内部队列中,当下一次屏幕更新时调用内部队列中的方法。那么“下一次屏幕更新”指的是什么呢?通过研究callLater方法的源代码我们发现callLater方法主要做三件事情:
首先将需要延迟调用的method方法及其参数args放入组件的内部队列中。
为舞台(Stage)对象的ENTER_FRAME和RENDER事件设置侦听器,在这两个事件的侦听器中会调用在“内部队列”中延迟的方法。队列中的方法一旦被调用就会从队列中清除。
调用舞台(stage)对象的invalidate()方法,这样FlashPlayer在显示列表渲染前能够派发RENDER事件。
因此,回顾图1-5所示的“FlashPlayer执行帧中ActionScript代码及渲染图形过程”,我们可以得出以下结论:如果在“用户动作”阶段的代码中调用callLater方法,则callLater方法中method参数所指定的方法将在“失效动作”阶段被调用。如果在“失效动作”阶段的代码中调用callLater方法,则callLater方法中的method参数所指定的方法将在下一帧ENTER_FRAME事件派发时调用。
<!-- CallLater.mxml -->
<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"
enterFrame="this.onEnterFrame(event)">
<mx:Script><![CDATA[
[Bindable]
public var text:String = "这是一串通过callLater方法实现滚动文字";
[Bindable]
//文字移动速度。
public var speed:Number = 5;
import flash.display.Stage
public function initTicker():void
{
//从右侧开始移动文字
theText.move( this.width+10, 0 );
callLater(moveText);
}
private function onEnterFrame(event:Event):void
{
trace("----------------进入新一帧------------------------");
}
public function moveText():void
{
var xpos:Number = theText.x;
if( xpos-speed+theText.width < 0 )
{
xpos = this.width+10; //从右侧开始移动文字
}
xpos -= speed;
//每帧应当执行两次该代码,分别在进入帧和渲染显示列表前各执行一次。
trace(speed, xpos);
//该代码的调用将导致显示列表失效,从而RENDER事件被Flash派发。
theText.move(xpos,0);
// 如果下面代码在"失效动作"阶段执行,则moveText方法将
在进入新一帧时被调用。如果该代码在进入新一帧时被调用,也
就是在"用户动作"阶段被调用,则moveText方法将在"失效动作"阶
段执行,也就是在渲染显示列表前执行。
callLater(moveText);
}
public function changeSpeed():void
{
speed = speedSelector.value;
}
]]>
</mx:Script>
<mx:Panel title="步进器例子" width="400"height="200">
<mx:Canvas creationComplete="initTicker()"
horizontalScrollPolicy="off"
backgroundColor="red" color="white"width="100%">
<mx:Label id="theText" text="{text}"y="0"/>
</mx:Canvas>
<mx:HBox>
<mx:Label text="速度:"/>
<mx:HSlider minimum="1" maximum="10"value="{speed}"
id="speedSelector" snapInterval="1" tickInterval="1"
change="changeSpeed()"/>
</mx:HBox>
</mx:Panel>
</mx:Application>
通过调试我们可以发现,在执行一帧的过程中,moveText()方法被执行两次,因为trace(speed,xpos)方法输出了两次。调试状态下,控制台输入如下所示:
----------------进入新一帧------------------------
5 1025
----------------进入新一帧------------------------
5 1020
5 1015
----进入新一帧------------------------------------
5 1010
5 1005
----------------进入新一帧------------------------
......