事件机制

发布时间:2015-10-29

  LayaFlash极高程度还原了AS3的事件机制,AS3开发者可以使用原有的开发习惯和技巧与LayaFlash H5开发无缝衔接。LayaFlash事件不支持捕获阶段之外其他方面与AS3的用法一致,包括事件的冒泡倘若已对AS3事件部分较为了解,可以跳过本节教程的内容。

  教程示例项目源码下载:

  layaFlashEvent.rar

  layaFlashEventBind.rar

  layaFlashEventDropTheCup.rar

1.事件机制原理和使用

事件机制可以让代码逻辑按照开发者事先预定的时机触发某个方法,与AS3的事件机制相同,LayaFlash的事件机制也包含以下几个要素:

1.1.注册和移除事件侦听器

1.2.事件类型

1.3.事件侦听器函数

1.4.事件对象

1.5.事件的派发

  每个事件触发过程都有以上几个要素参与,只要对象继承了EventDispatch类就能侦听和派发事件。

  以下代码示例给舞台添加一个鼠标事件(教程示例项目layaFlashEvent):

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    
    public class Main extends Sprite
    {
        public function Main():void
        {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void
        {
            removeEventListene(Event.ADDED_TO_STAGE, init);
            this.stage.addEventListener(MouseEvent.CLICK, stageClickHandler);
        }
        
        private function stageClickHandler(event:MouseEvent):void
        {
            this.stage.removeEventListener(MouseEvent.CLICK, stageClickHandler);
            trace("stage is clicked.");
        }
    }
}

1.1.注册和移除事件侦听器

  LayaFlash IDE编译运行,点击舞台,可以看到调试工具的控制台输出了“stage is clicked.”:

1.png

  代码示例中使用addEventListener方法给舞台添加了一个点击事件MouseEvent.CLICK

this.stage.addEventListener(MouseEvent.CLICK, stageClickHandler);

  stageClickHandler方法叫事件侦听器函数:

private function stageClickHandler(event:MouseEvent):void
{
    trace(event.target + " is clicked.");
}

  这样只要有MouseEvent.CLICK 事件被派发,stageClickHandler就会被调用。实际上示例代码里的init方法也是一个事件侦听器函数,只不过它侦听的是Event.ADDED_TO_STAGE事件。有注册必然有移除,addEventListener对应的是removeEventListener方法,调用它移除事件:

this.stage.removeEventListener(MouseEvent.CLICK,stageClickHandler);

  须注意LayaFlash不能像AS3那样使用弱监听和事件优先级参数,以下代码的最后两个参数即使传递了在H5中不会生效:

this.stage.removeEventListener(MouseEvent.CLICK,
stageClickHandler,true, 2);

  定义事件侦听器函数的父对象如果和代码当前的对象不一致,需要使用bind方法先绑定作用域,然后再传入addEventListener方法。例如要添加事件侦听器函数的不是Main类的stageClickHandler方法,而是另一个自定义类StageChild中的方法就需要修改一下注册侦听器函数的代码(教程示例项目layaFlashEventBind)。

  StageChild类的代码:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import iflash.method.bind;
	import listenerDemo.StageChild;
	
	public class Main extends Sprite
	{
		public function Main():void
		{
			if (stage)
				init();
			else
				addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			var child:StageChild = new StageChild();
			this.stage.addEventListener(MouseEvent.CLICK, 
			child.stageClickHandler);
		}
	}
}

  修改Maininit的代码:

private function init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
    var child:StageChild = new StageChild();
    this.stage.addEventListener(MouseEvent.CLICK, 
    bind(child, child.stageClickHandler));
}

  bind方法让事件处理器函数的作用域和定义它的StageChild对象关联。在移除事件时也使用bind函数:

this.stage.removeEventListener(MouseEvent.CLICK,
bind(child, child.stageClickHandler));

  更多关于bind方法的内容请参看《LayaFlash新增方法》中的“函数作用域绑定:bind()”的内容。

1.2.事件类型

  示例项目代码用到的MouseEvent.CLICK是事件类型,这个事件类型通常是系统派发的事件,开发者也可以调用dispatchEvent()方法派发这些事件,如何派发事件会在下面的内容涉及。

 1.3.事件对象

  每个事件侦听器函数在定义的时候必须带有一个方法参数:

1c.png

  event参数就是事件对象,此对象保存事件派发时传递到事件侦听器函数的信息,这些信息包含了事件侦听器函数要处理逻辑所用的数据。我们可以通过event.target属性和event.currentTarget属性获取到事件的派发者,并在事件处理器函数中访问事件派发者的属性。

 1.4.事件的派发

  如前所述,系统事件的派发通常是由系统负责,上述例子也是系统监听到用户点击舞台的操作才派发了一个MouseEvent.CLICK事件,才触发了stageClickHandler的逻辑,系统派发的代码在AS3是不可见的,但在LayaFlash里可以在EventDispatcher类看到代码实现,派发一个事件的代码为:

dispatchEvent(new MouseEvent(MouseEvent.CLICK));

  派发事件就像发送江湖暗号,暗号一出,事先知道暗号所代表意思的一方就做出相应的行动。


2.自定义事件

  我们使用的事件可以是系统现有的事件,也可以是自定义的事件,使用自定义类型的事件叫自定义事件。

  三国演义里周瑜就曾在和刘备开会的时候想“掷杯为号”让刀斧手埋伏刘备,这“掷杯”就是事件,刀斧手事先被周瑜“注册”了这个事件,一旦周瑜真的“掷杯”,刀斧手就出来了。我们就以周瑜“掷杯为号”为例,说明一下如何使用自定义事件(示例项目layaFlashEventDropTheCup)。

  代表周瑜的将军类General.as

package myEvent
{
    import flash.events.EventDispatcher;
    
    public class General extends EventDispatcher
    {
        public function General()
        {
            super();
        }
        
        /**
         * 掷杯为号
         *
         */
        public function dropCupAsSign(targetName:String):void
        {
            var event:KillEvent = new KillEven(KillEvent.DROP_THE_CUP_SIGN);
            event.killTargetName = targetName;
            this.dispatchEvent(event); //派发事件       
        }
    }
}

  代表刀斧手的士兵类Solders.as:

package myEvent
{
    import flash.events.EventDispatcher;
    
    public class Soldiers extends EventDispatcher
    {
        public function Soldiers()
        {
            super();
        }
        
        /**
         * 突然冲出来并干掉目标,这里是具体响应事件的侦听器函数
         *
         * @param event
         *
         */
        public function rushAndKillSomeOne(event:KillEvent):void
        {
            trace("kill the " + event.killTargetName + "!");
        }
    }
}

  自定义的事件类KillEvent.as:

package myEvent
{
    import flash.events.Event;
    
    public class KillEvent extends Event
    {
        public static const DROP_THE_CUP_SIGN:String = "dropTheCupSignEvent";
        public var killTargetName:String;
        
        public function KillEvent(type:String, 
        bubbles:Boolean = false, cancelable:Boolean = false)
        {
            super(type, bubbles, cancelable);
        }
    }
}

  修改Main.as中的代码:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import iflash.method.bind;
	import myEvent.General;
	import myEvent.KillEvent;
	import myEvent.Soldiers;
	
	public class Main extends Sprite
	{
		public function Main():void
		{
			if (stage)
				init();
			else
				addEventListener(Event.ADDED_TO_STAGE, init);
		}
		private var _general:General; //周瑜
		private var _solders:Soldiers; //刀斧手
		
		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			_general = new General();
			_solders = new Soldiers();
			_general.addEventListener(KillEvent.DROP_THE_CUP_SIGN,
			bind(_solders, _solders.rushAndKillSomeOne));
			waitingSign(); //等待周瑜信号
		}
		
		private function waitingSign():void
		{
			this.stage.addEventListener(MouseEvent.CLICK, 
			stageClickHandler);
		}
		
		private function stageClickHandler(event:MouseEvent):void
		{
			this.stage.removeEventListener(MouseEvent.CLICK, 
			stageClickHandler);
			_general.dropCupAsSign("刘备"); //触发事件
			//移除自定义的事件
			_general.removeEventListener(
				KillEvent.DROP_THE_CUP_SIGN, 
				bind(_solders, _solders.rushAndKillSomeOne)); 
		}
	}
}

  用LayaFlash编译代码后运行,点击舞台后可以看到LayaFlash IDE输出:

2.png

  这里让用户扮演了一次周瑜,只要点击舞台就会让“周瑜”发出“掷杯”信号,让事先添加了自定义事件KillEvent.DROP_THE_CUP_SIGN的“刀斧手”做他们该做的事情。由于自定义的事件类定义了一个属性killTargetName,这是发送给事件侦听器函数的数据,从而影响执行逻辑时的结果。

  事件不再使用时要及时移除,这跟AS3中使用事件时一样,大量没有使用的事件不及时移除会引发内存问题。被移除的事件除非被再次添加,否则不会生效。