イベントリスナー

Flash Player 9 以降、Adobe AIR 1.0 以降

イベントハンドラーとも呼ばれるイベントリスナーは、Flash Player および AIR が特定のイベントに応答して実行する関数です。イベントリスナーを登録するには 2 つの手順を実行します。 まず、目的のイベントに応答して Flash Player または AIR に実行させるための関数またはクラスメソッドを作成します。これは、リスナー関数またはイベントハンドラー関数と呼ばれることもあります。 次に addEventListener() メソッドを使用して、そのリスナー関数を、イベントのターゲットまたは該当するイベントフローに含まれる任意の表示リストオブジェクトに登録します。

リスナー関数の作成

ActionScript 3.0 のイベントモデルのうち、リスナー関数の作成に関する部分は DOM イベントモデルに準拠していません。 DOM イベントモデルではイベントリスナーとリスナー関数が明確に区別されています。イベントリスナーは EventListener インターフェイスを実装するクラスのインスタンスで、リスナー関数はその handleEvent() というクラスのメソッドです。DOM イベントモデルにおいては、リスナー関数自体を登録するのではなく、リスナー関数を含んだクラスインスタンスを登録する形式をとります。

ActionScript 3.0 のイベントモデルには、イベントリスナーとリスナー関数との区別がありません。 ActionScript 3.0 には EventListener インターフェイスがなく、リスナー関数はクラス内、クラス外のいずれに定義することもできます。 さらに、リスナー関数は handleEvent() で指定する必要がありません。任意の有効な識別子を使用して指定することができます。ActionScript 3.0 においては、リスナー関数自体の名前を登録する形式をとります。

クラス外に定義したリスナー関数

次のコードは、赤い正方形のシェイプを表示する単純な SWF ファイルです。 クラスの一部ではない clickHandler() という名前のリスナー関数は、赤い正方形上のマウスクリックイベントを監視します。

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, clickHandler); 
    } 
} 
 
function clickHandler(event:MouseEvent):void 
{ 
    trace("clickHandler detected an event of type: " + event.type); 
    trace("the this keyword refers to: " + this); 
}

ユーザーが、正方形をクリックして生成される SWF ファイルを操作すると、Flash Player または AIR は以下のトレース出力を生成します。

clickHandler detected an event of type: click 
the this keyword refers to: [object global]

イベントオブジェクトは、引数として clickHandler() に渡されるようになっています。これにより、リスナー関数の中でイベントオブジェクトを調べることができます。 この例では、イベントオブジェクトの type プロパティを使用し、イベントがクリックイベントであることを確認しています。

また、この例では this キーワードの値も確認しています。 this はグローバルオブジェクトを参照しています。なぜなら、リスナー関数をカスタムクラスやオブジェクトに属さない外部で定義しているからです。

クラスメソッドとして定義されたリスナー関数

次の例では、前出の例と同じく ClickExample クラスを定義していますが、 clickHandler() 関数を ChildSprite クラスのメソッドとして定義している点が異なります。

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, clickHandler); 
    } 
    private function clickHandler(event:MouseEvent):void 
    { 
        trace("clickHandler detected an event of type: " + event.type); 
        trace("the this keyword refers to: " + this); 
    } 
}

ユーザーが、赤い正方形をクリックして生成される SWF ファイルを操作すると、Flash Player または AIR は以下のトレース出力を生成します。

clickHandler detected an event of type: click 
the this keyword refers to: [object ChildSprite]

this キーワードは child という名前の ChildSprite インスタンスを参照するようになっています。これは ActionScript 2.0 からビヘイビアーが変更されています。ActionScript 2.0 のコンポーネントでは、 UIEventDispatcher.addEventListener() に対してクラスメソッドを指定した場合、リスナーメソッドのスコープが、メソッドが定義されているクラスではなくイベントをブロードキャストしたコンポーネントに基づいて決まるようになっていました。つまり、この例と同じテクニックを ActionScript 2.0 で使用したとすると、 this キーワードは、ChildSprite インスタンスではなくイベントをブロードキャストしたコンポーネントを参照することになります。

この従来の仕様では、リスナーメソッドからそのメソッドがあるクラス内の他のメソッドやプロパティにアクセスできないため、場合によっては非常に大きな問題が生じていました。 これを回避するために、ActionScript 2.0 には、 mx.util.Delegate クラスを使用してリスナーメソッドのスコープを変更するという方法が用意されていました。ActionScript 3.0 では addEventListener() の呼び出し時にバインドメソッドが作成されるため、この回避策は不要です。これにより、 this キーワードで child という名前の ChildSprite インスタンスを参照し、プログラムから ChildSprite クラスの他のメソッドとプロパティにアクセスできるようになります。

望ましくないイベントリスナー登録の方法

第 3 のテクニックとして、汎用オブジェクトを作成し、そのオブジェクトのプロパティによって動的にリスナー関数を割り当てる方法がありますが、これを使用することはお勧めできません。 ActionScript 2.0 でよく使用されますが、ActionScript 3.0 で使用するのは望ましくないので、この章で説明しています。この方法は this キーワードがリスナーオブジェクトの代わりにグローバルオブジェクトを参照するので、お勧めできません。

次の例では、前出の例と同じく ClickExample クラスを定義していますが、リスナー関数を myListenerObj という汎用オブジェクトの一部として定義している点が異なります。

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, myListenerObj.clickHandler); 
    } 
} 
 
var myListenerObj:Object = new Object(); 
myListenerObj.clickHandler = function (event:MouseEvent):void 
{ 
        trace("clickHandler detected an event of type: " + event.type); 
        trace("the this keyword refers to: " + this); 
}

トレースの結果は次のようになります。

clickHandler detected an event of type: click 
the this keyword refers to: [object global]

this myListenerObj を参照し、その結果トレース出力が [object Object] となると考えられる場合でも、this キーワードはグローバルオブジェクトを参照します。動的なプロパティ名を addEventListener() に引数として渡す場合、Flash Player または AIR はバインドメソッドを作成できません。なぜなら、その場合に listener パラメーターとして渡されるのはリスナー関数の単なるメモリアドレスであり、Flash Player および AIR がそのメモリアドレスと myListenerObj インスタンスを結び付ける手段がないからです。

イベントリスナーの管理

リスナー関数を管理するには、IEventDispatcher インターフェイスのメソッドを使用します。 IEventDispatcher インターフェイスは、DOM イベントモデルにおける EventTarget インターフェイスの ActionScript 3.0 バージョンです。 IEventDispatcher という名前からは、Event オブジェクトを送信(送出)することが主な目的であるように思われますが、実際には、イベントリスナーを登録、確認、および削除する目的でメソッドを使用する機会が最も多いといえます。 IEventDispatcher インターフェイスは、次のコードに示すように、5 つのメソッドを定義します。

package flash.events 
{ 
    public interface IEventDispatcher 
    { 
        function addEventListener(eventName:String,  
                        listener:Object, 
                        useCapture:Boolean=false, 
                        priority:Integer=0, 
                        useWeakReference:Boolean=false):Boolean; 
 
        function removeEventListener(eventName:String,  
                    listener:Object, 
                    useCapture:Boolean=false):Boolean; 
 
        function dispatchEvent(eventObject:Event):Boolean; 
 
        function hasEventListener(eventName:String):Boolean; 
        function willTrigger(eventName:String):Boolean; 
    } 
}

Flash Player API により IEventDispatcher インターフェイスが EventDispatcher クラスに実装され、イベントターゲットまたはイベントフローの一部になることができるすべてのクラスの基本クラスとして機能します。 例えば、DisplayObject クラスは EventDispatcher クラスを継承しています。 つまり、表示リストの任意のオブジェクトが IEventDispatcher インターフェイスのメソッドにアクセスできることを意味します。

イベントリスナーの追加

addEventListener() は、IEventDispatcher インターフェイスで最もよく使用されるメソッドです。リスナー関数を登録する際にはこのメソッドを使用します。 type および listener の 2 つのパラメーターは必須です。 type パラメーターでは、イベントのタイプを指定します。 listener パラメーターでは、目的のイベントが発生したときに実行するリスナー関数を指定します。 listener パラメーターには、関数への参照またはクラスメソッドへの参照のいずれかを指定できます。

listener パラメーターを指定する際には、括弧を使用しないでください。例えば、次の addEventListener() メソッド呼び出しで、 clickHandler() 関数は括弧なしで指定されます。
addEventListener(MouseEvent.CLICK, clickHandler)

addEventListener() メソッドの useCapture パラメーターを使用すると、イベントフローのどの段階においてリスナーをアクティブにするかを制御できます。 useCapture true を指定すると、そのリスナーはイベントフローのキャプチャ段階でアクティブになります。 useCapture false を指定すると、そのリスナーはイベントフローのターゲット段階とバブリング段階でアクティブになります。イベントフローのすべての段階でイベントを受け取るには、 addEventListener() を 2 回、つまり useCapture true に設定して 1 回、 false に設定して 1 回呼び出す必要があります。

addEventListener() メソッドの priority パラメーターは、DOM Level 3 イベントモデルの正式なパラメーターではありません。これは、イベントリスナーをより柔軟に編成できるようにするため、ActionScript 3.0 で独自に提供されているパラメーターです。 addEventListener() を呼び出す際に priority パラメーターに整数値を指定することで、イベントリスナーの優先度を設定できます。デフォルト値は 0 ですが、負の整数値または正の整数値を指定することができます。 指定した数値が高いほど、イベントリスナーが早い順序で実行されます。 同じ優先度で登録された複数のイベントリスナーがある場合、それらは登録順に実行されます。つまり、先に登録したイベントリスナーほど早く実行されます。

useWeakReference パラメーターを使用すると、そのリスナー関数に対する参照を通常の参照にするか、弱参照にするかを指定できます。このパラメーターに true を指定すると、リスナー関数が使用されなくなった後もメモリ上に残り続けるのを防ぐことができます。Flash Player および AIR では、ガベージコレクションというテクニックによって、使用されなくなったオブジェクトをメモリから削除します。あるオブジェクトに対して参照が 1 つも存在しなくなったとき、そのオブジェクトはもう使用されないと見なされます。 弱参照はガベージコレクションのシステムで無視されるため、弱参照による参照しか受けていないリスナー関数はガベージコレクションの対象となります。

イベントリスナーの削除

不要になったイベントリスナーを削除するには、 removeEventListener() メソッドを使用します。不要になったリスナーは削除することをお勧めします。必須パラメーターには eventName および listener があります。これらは、 addEventListener() メソッドの必須パラメーターと同じです。前述したとおり、イベントフローのすべての段階においてイベントを受け取る場合は、 useCapture true を指定した場合と false を指定した場合の両方について、合計 2 回 addEventListener() を呼び出します。両方のイベントリスナーを削除するには、 removeEventListener() を 2 回、つまり useCapture true に設定して 1 回、 false に設定して 1 回呼び出す必要があります。

イベントの送出

dispatchEvent() メソッドは、独自のイベントオブジェクトをイベントフローに送出する場合に使用する上級開発者向けのメソッドです。このメソッドに指定できるパラメーターは 1 つだけで、Event クラスまたはそのサブクラスのインスタンスを指定します。 イベントを送出すると、イベントオブジェクトの target プロパティが dispatchEvent() を呼び出したオブジェクトに設定されます。

既存のイベントリスナーの確認

IEventDispatcher インターフェイスが備える残り 2 つのメソッドは、イベントリスナーの存在に関する有用な情報を取得するために使用します。 指定した表示リストオブジェクトに、指定したイベントタイプのイベントリスナーが登録されている場合、 hasEventListener() メソッドは true を返します。 willTrigger() メソッドも、指定した表示リストオブジェクトについてリスナーが登録されている場合に true を返します。ただし、 willTrigger() では指定した表示オブジェクト自体を確認するだけでなく、イベントフローのすべての段階におけるその表示リストオブジェクトの祖先もすべて確認します。

エラーイベントのリスナーを登録しない場合

エラー処理に関して、ActionScript 3.0 における最も重要なメカニズムはイベントではなく例外ですが、非同期の操作(ファイルのロードなど)には例外処理を使用できません。 非同期の操作を実行中にエラーが発生すると、Flash Player および AIR によりエラーイベントオブジェクトが送出されます。エラーイベントに対するリスナーを作成していない場合、デバッグ版の Flash Player および AIR では、発生したエラーに関する情報がダイアログボックスに表示されます。例えば、Flash Player のデバッガーのバージョンでは、アプリケーションが無効な URL からファイルをロードしようとした場合のエラーを説明する次のダイアログボックスを生成します。

エラーイベントのほとんどは ErrorEvent クラスに基づいているため、Flash Player または AIR で表示するためのエラーメッセージを格納する text というプロパティがあります。ただし、StatusEvent クラスと NetStatusEvent クラスの 2 つはこれに該当しません。 これらのクラスには level プロパティ( StatusEvent.level および NetStatusEvent.info.level )があります。 level プロパティの値が「 error 」の場合、これらのイベントタイプはエラーイベントと見なされます。

エラーイベントが発生しても、それによって SWF ファイルの実行が停止することはありません。 エラーイベントは、ブラウザープラグインやスタンドアローンプレーヤーのデバッガーバージョンではダイアログボックスとして、オーサリングプレーヤーの出力パネルにはメッセージとして、Adobe Flex Builder ではログファイルのエントリとして表示されるだけです。Flash Player または AIR のリリース版では一切表示されません。