LayaFlash极高程度还原了AS3的事件机制,AS3开发者可以使用原有的开发习惯和技巧与LayaFlash H5开发无缝衔接。LayaFlash事件不支持捕获阶段之外其他方面与AS3的用法一致,包括事件的冒泡。倘若已对AS3事件部分较为了解,可以跳过本节教程的内容。
教程示例项目源码下载:
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.”:
代码示例中使用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); } } }
修改Main里init的代码:
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.事件对象
每个事件侦听器函数在定义的时候必须带有一个方法参数:
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输出:
这里让用户扮演了一次周瑜,只要点击舞台就会让“周瑜”发出“掷杯”信号,让事先添加了自定义事件KillEvent.DROP_THE_CUP_SIGN的“刀斧手”做他们该做的事情。由于自定义的事件类定义了一个属性killTargetName,这是发送给事件侦听器函数的数据,从而影响执行逻辑时的结果。
事件不再使用时要及时移除,这跟AS3中使用事件时一样,大量没有使用的事件不及时移除会引发内存问题。被移除的事件除非被再次添加,否则不会生效。