AS2のEventDispatcher
- 2009 年 3月 11 日
随分前、同じようなエントリーしたきもするけど実務では未だAS3とAS2が混在した状況。作り手としては、AS3で作る時もAS2で作る時もなるべく同じような作り方でいきたいもんだ。そこでAS3とAS2でイベントモデルの違いが顕著なのですが、この差をなるべく埋めるべく、今さらながらAS2のEventDispatcherを見直してみます。
僕はAS3に移行する前はイベントのキャストはAsBroadcasterを使っていて、EventDispatcherは使っていませんでした。正直知りませんでしたってくらい。もっといえば、いわゆるAS1.5的な作り方だとイベントリスナー自体、あまり使っていなかったし。今でもタイムライン使っていんじゃね?みたいなことは一杯ある気もします。
しかし、改めて比べてみるとEventDispatcherの方がソースがすっきりするね。
さらにAS2のCASA Frameworkにある拡張機能を使った場合も比べてみます。
アニメーションが完了したらイベントを通知するオブジェクトSquareを作ります。
Squareから通知されたイベントを受け受け取るオブジェクトIndexを作ります。
Squareがイベント通知する方法をAsBroadCasterとEventDispatcherとCasa Frameworkで比べてみます。
AS3のEventクラスに習って定数をCOMPLETEを設定したEvent.asを用意します。
これは全部の方法で同じものを使います。
Event.as
class Event{ static var COMPLETE:String = "complete"; } |
AsBroadcasterの場合
Square.as
import Event; import caurina.transitions.Tweener; class Square extends MovieClip{ public function Square(){ AsBroadcaster.initialize( this ); this.onPress = this.onPressHandler; } public function onPressHandler():Void{ Tweener.addTween( this, { _xscale:0, _yscale:0, _rotation:360, transition:"easeInSine", time:1, onComplete:function():Void{ this.broadcastMessage( Event.COMPLETE, this ) this.removeMovieClip(); } }); } } |
Index.as
import Event; class Index extends MovieClip{ public function Index() { this.listener = { scope:this }; this.listener[ Event.COMPLETE ] = function(e:Object){ this.scope.attach(); e.removeListener( this ) } this.attach(); } public var square:MovieClip; public function completeHandler(e:Object):Void{ this.attach(); } public function attach():Void{ this.square = this.attachMovie( "Square", "square", this.getNextHighestDepth(), { _x:200, _y:200 } ); this.square.addListener( listener ); } public var listener:Object; } |
まどろっこしいソースになってますが、変数宣言せずにaddListenerに直接オブジェクト入れちゃうとか短く各方法は沢山あると思います。AsBroadcasterの場合は、リスナーにオブジェクトを使います。他と比べてリスナーで処理を実行する時のスコープをどうもってくるかがポイントになるかと思う。自分的にはAsBroadcasterを使うことが多いのだけれど、EventDispatcherより便利と思うところの一つあってリスナーは_listenerって配列の中に格納されるので、一度にリスナーを消すってのがやりやすいと思います。
EventDispatcherの場合
Square.as
import Event; import caurina.transitions.Tweener; import mx.events.EventDispatcher; class Square extends MovieClip{ public function Square(){ EventDispatcher.initialize( this ); this.onPress = this.onPressHandler; } public function onPressHandler():Void{ Tweener.addTween( this, { _xscale:0, _yscale:0, _rotation:360, transition:"easeInSine", time:1, onComplete:function():Void{ this.dispatchEvent( { type:Event.COMPLETE, target:this } ); this.removeMovieClip(); } }); } } |
dispatchEventの時、オブジェクトを使うのとイベントの種類をtypeで渡すのは、AsBroadcasterとの差異ですね。
Index.as
import Event; import mx.utils.Delegate; class Index extends MovieClip{ public function Index() { this.attach(); } public var square:MovieClip; public function completeHandler(e:Object):Void{ e.target.removeEventListener( e.type, arguments.callee ); this.attach(); } public function attach():Void{ this.square = this.attachMovie( "Square", "square", this.getNextHighestDepth(), { _x:200, _y:200 } ); this.square.addEventListener( Event.COMPLETE, Delegate.create( this, this.completeHandler ) ); } } |
受け取る方は、AS3に近い。リスナー関数のスコープがaddEventListenerを定義しているオブジェクトそのものになるので、ここではDelegateを使ってスコープを変えています。大抵、そうなるんじゃないかと思う。AsBroadcasterでいうところの_listenerが無くて、設定したリスナーを一つずつremoveEventListenerしないといけないのではと思います。
CASA FrameWorkの場合
Square.as
import Event; import caurina.transitions.Tweener; import org.casalib.movieclip.EventMovieClip class Square extends EventMovieClip{ public function Square(){ this.onPress = this.onPressHandler; } public function onPressHandler():Void{ Tweener.addTween( this, { _xscale:0, _yscale:0, _rotation:360, transition:"easeInSine", time:1, onComplete:function():Void{ this.dispatchEvent( Event.COMPLETE, this ); this.removeMovieClip(); } }); } } |
ここではEventMovieClipを継承していますが、EventMovieClilpが継承しているDispatchableMovieClipでも問題ない。EventMovieClipだとEventMovieClipに定義されているイベント通知が予め追加されます。たとえばロールオーバーなら、 EventMovieClip.EVENT_ROLL_OUTで以下のようにキャッチして処理できる。
this.square.addEventObserver( this, EventMovieClip.EVENT_ROLL_OUT, "rollOverHandler"); |
継承する方法以外にもCASAのMovieClipUtilクラスを使えば、アタッチする時にEventMovieClipクラス等を割り当てたりすることも出来る。
CASAでリスナーを設定する時は、CASAが拡張するメソッドaddEventObserverを使います。
Index.as
import Event; import org.casalib.movieclip.EventMovieClip class Index extends MovieClip{ public function Index() { this.attach(); } public var square:MovieClip; public function completeHandler(e:Object):Void{ e.removeAllEventObservers(); this.attach(); } public function attach():Void{ this.square = this.attachMovie( "Square", "square", this.getNextHighestDepth(), { _x:200, _y:200 } ); this.square.addEventObserver( this, Event.COMPLETE, "completeHandler" ); } } |
ここでは全てのリスナーを削除するremoveAllEventObserversを使いましたが、removeEventListenerにあたるメソッドは勿論、特定のイベントタイプのリスナーを全て削除したり、あるスコープ内のリスナーを全て削除するメソッドも用意されています。
改めてクラスベースって考えてAS2を見てみると、CASA便利だなぁと思った。