利用render事件来提高as3程序的运行效率

 

DisplayObject有一个render事件,他会在重绘DisplayList之前触发,这给我们提供了在重绘之前最后一次操作的机会。
每次需要DisplayObject触发render事时,都要调用一次 stage.invalidate();


下面用一个小例子来说明一下具体用法把。


假设我们现在要写一个list组件,该组件有addItem()方法用于添加list项目,和remvoeItem() 方法用于删除list项目,当然还可能有addItemAt(),removeItemAt()等方法,这些方法调用后,都需要对list内的显示对象进 行重新排列。
我们先实现一个List类,用于显示列表项目
List类中,有addItem() 和 removeItem() 这两个方法提供给外部调用,用于添加和删除list项目,这两个方法中除了将列表项目添加/删除,还要调用一个方法来重新对list中的项目进行排列,layoutContents()。
关键就是,这个layoutContents()的调用,他的调用次数越少,那效率当然就越高啦,如果是常规的做法,就是类似这样:

利用render事件来提高as3程序的运行效率 - Davis - GotoAndPlay部落 代码
public  function addItem(item:DisplayObject): void  {
    addChild(item);
    layoutContents();
}

将item加入后,重新排列列表

下面是List类的源代码:

利用render事件来提高as3程序的运行效率 - Davis - GotoAndPlay部落 代码
package
{
    
import  flash.display.DisplayObject;
    
import  flash.display.Sprite;
    
import  flash.events.Event;

    
public   class  List  extends  Sprite
    {
        
public  function addItem(item:DisplayObject): void
        {
            addChild(item);
            layoutContents();
        }

        
public  function removeItem(item:DisplayObject): void
        {
            
if  (contains(item))
            {
                removeChild(item);
                layoutContents();
            }
        }
        
// 对内部项目进行排列,可以是任意的排列算法
         protected  function layoutcontents(): void
        {
            trace(
" do layout " );
            var y:Number 
=   0 ;
            var num:
int   =  numChildren;
            
for  (var i: int   =   0 ; i  <  num; i ++ )
            {
                var child:DisplayObject 
=  getChildAt(i);
                child.x 
=   0 ;
                child.y 
=  y;

                y 
+=  child.height  +   2 ;
            }
        }
    }
}

这个程序粗看似乎没什么问题,但却存在一个效率问题
如果只调用一次addItem,没问题,如果调用10次呢? 前9次的layoutcontents()都不是必须的,只有第十次才是真正需要的这样程序的效率就降低了。
我们可以试一下

先需要一个简单的ListItem

利用render事件来提高as3程序的运行效率 - Davis - GotoAndPlay部落 代码
package
{
    
import  flash.display.Shape;
    
public   class  ListItem  extends  Shape
    {
        
public  function ListItem()
        {
            
super ();
            graphics.beginFill(
0xFF6600 );
            graphics.drawRect(
0 0 30 16 );
            graphics.endFill();
        }
    }
}

然后测试

利用render事件来提高as3程序的运行效率 - Davis - GotoAndPlay部落 代码
package
{
    
import  flash.display.Sprite;

    
public   class  ListTest  extends  Sprite
    {
        
public  function ListTest()
        {
            var list:List 
=   new  List();
            addChild(list);
            list.addItem(
new  ListItem());
            list.addItem(
new  ListItem());
            list.addItem(
new  ListItem());
        }
    }
}

 

我们可以看到,输出了3次 do layout 说明layoutcontents执行了3次,前两次都是多余的。

现在,解决办法就是利用render事件啦。

因为在当前帧内,显示列表更新前会触发render事件,所以在render事件触发后来排列列表项目,就可以保证排列方法在做了任意次的添加或删除操作后只需调用一次,从而提高效率。

这么做只需要对List类稍做一些改动,首先肯定是要监听render事件,我们可以仅监听stage对象的render事件即可,因为这样以后可以做一个独立的RepaintManger来管理所有组件的重绘(可以参考AsWing的RepaintManager类)。
在render事件触发后,做我们需要的调整,由于要render事件触发,就必须先调用stage.invalidate() ,所以每次添加或删除list项目后,都要执行一次该方法,即:

代码:

 

利用render事件来提高as3程序的运行效率 - Davis - GotoAndPlay部落 代码
public  function addItem(item:DisplayObject):voide
{
//
    stage.invalidate()
}

由于是监听的stage的render事件,所以在添加删除操作后,要做一个标记,表示list有改动,需要在render事件后重新排列,如果该标记为 false,那么即使render触发了也不做排列,因为stage的render事件也有可能是由于该stage内的其他child需要重绘而造成 stage的render触发。
下面是改过后的List代码

利用render事件来提高as3程序的运行效率 - Davis - GotoAndPlay部落 代码
package
{
    
import  flash.display.DisplayObject;
    
import  flash.display.Sprite;
    
import  flash.events.Event;

    
public   class  List  extends  Sprite
    {

        
private  var changed:Boolean;

        
public  function List()
        {
            
super ();
            addEventListener(Event.ADDED_TO_STAGE, __addToStage);
        }

        
public  function addItem(item:DisplayObject): void
        {
            addChild(item);
            requireLayout();
        }

        
public  function removeItem(item:DisplayObject): void
        {
            
if  (contains(item))
            {
                removeChild(item);
                requireLayout();
            }
        }

        
private  function requireLayout(): void
        {
            changed 
=   true ;
            
if  (stage  !=   null )
                stage.invalidate();
        }

        
// 对内部项目进行排列,可以是任意的排列算法
         protected  function layoutContents(): void
        {
            trace(
" do layout " );
            var y:Number 
=   0 ;
            var num:
int   =  numChildren;
            
for  (var i: int   =   0 ; i  <  num; i ++ )
            {
                var child:DisplayObject 
=  getChildAt(i);
                child.x 
=   0 ;
                child.y 
=  y;
                y 
+=  child.height  +   2 ;
            }
        }

        
private  function __addToStage(e:Event): void
        {
            stage.addEventListener(Event.RENDER, __render);
            
if  (changed)
                stage.invalidate();
        }

        
private  function __render(e:Event): void
        {
            
if  (changed)
            {
                layoutContents();
                changed 
=   false ;
            }
        }
    }
}

 

当我们再次运行ListTest的时候,do layout 只输出了一次。

就是这些内容,当然,你可能会说,需要做到这些根本不需要这么复杂,只要公开layoutContents方法,在所有操作调用之后让调用者自行调用一次layoutContents()。
在 这个例子中当然可以,但是当情况很复杂的时候,使用者每进行一次操作都要自行调用更新的方法,这样做并不是好的解决方案。试想,如果 flashplayer不会为我们处理显示DisplayObject的工作,而是每次addChild/removeChild之后,我们都需要自行调 用flashplayer底层的方法来让我们需要的东西显示出来,这样做显然很不好。

你可能感兴趣的:(算法,工作,list,测试,layout,null)