web-dev-qa-db-ja.com

AS3のイベントリスナーに簡単な方法でパラメーターを渡すには...存在しますか?

予想/擬似例:

stage.addEventListener(MouseEvent.CLICK, onClick.someWayToPassParameters(true, 123, 4.56, "string"));
function onClick(e:MouseEvent):void {
    trace("Received " + someWayToRetrieveParameters().b/i/n/s + ".");
}

長年(3〜4)、すべてのウェブサイト、フォーラム、ブログで、私が検索したところはどこでも、人々はこれを行う簡単な方法はないと言っています。彼らは通常次のことを提案します:

  • リスナーを動的オブジェクトに追加します。ここで、値を追加のプロパティに設定し、関数でそれを参照できます(e.target.property/e.currentTarget.property)。

    すべてのクラスが動的ではありません。たとえば、スプライトでは機能しません。

  • オブジェクトのクラスをカスタムクラスで拡張して、プロパティを追加するか、単に動的にします。

    毎回まったく新しいTweakクラスを作成する必要があります。

  • 匿名関数をイベントハンドラとして使用します。

    参照はありません(そして見苦しいです)。リスナーを削除してリソースを解放するには、関数自体の内部からarguments.calleeを使用して強制的に解放します。

  • イベントハンドラー内で、パラメーターを使用して別の関数を呼び出します。

    そして、イベントハンドラーコールのどこにパラメーターが入りますか?

  • イベントハンドラーをパラメーターと同じスコープに保ちます。

    全体的な意味の混乱への違反。

  • ターゲットとパラメータを受け取る関数でイベントハンドラー定義とaddEventListener呼び出しの両方をカプセル化します。

    スコープを混在させることもできますが、近いものです。ただし、注意する必要があります。

...他の多くの推奨される回避策の中でも。

私が望んでいるのは、イベントハンドラに引数を渡すだけで、通常の関数と同じように関数内で使用できるようにするだけです!

頼みすぎですか?

23
Trovs

箱から出して:2行のエレガントなコードだけでこの古代のパズルを解きます。

stage.addEventListener(MouseEvent.CLICK, onClick(true, 123, 4.56, "string"));
function onClick(b:Boolean, i:int, n:Number, s:String):Function {
  return function(e:MouseEvent):void {
    trace("Received " + b + ", " + i + ", " + n + " and " + s + ".");
  };
}

しかし最も重要なことは、リソースを解放するために後でリスナーを削除する必要がある可能性が非常に高いことです。そのため、変数に+ 1行で格納を指定します。

var functionOnClick:Function = onClick(true, 123, 4.56, "string");
stage.addEventListener(MouseEvent.CLICK, functionOnClick);
function onClick(b:Boolean, i:int, n:Number, s:String):Function {
  return function(e:MouseEvent):void {
    trace("Received " + b + ", " + i + ", " + n + " and " + s + ".");
  };
}

そして、あなたは通常それを削除することができます:

trace("Before: " + stage.hasEventListener(MouseEvent.CLICK));
stage.removeEventListener(MouseEvent.CLICK, functionOnClick);
trace("After: " + stage.hasEventListener(MouseEvent.CLICK));

以下は、その使用を証明するための、より詳細で動的な例です。

function onClick(s:String):Function {
  return function(e:MouseEvent):void {
    trace("The square " + s + " at x = " + e.currentTarget.x + "px was clicked");
  };
}
var myFunctions:Array = new Array();
for (var i:int = 0; i < 10; i++) {
  myFunctions.Push(onClick("#" + (i+1)));
}
for (i = 0; i < myFunctions.length; i++) {
  var square:Sprite = new Sprite();
  square.name = "sqr" + i;
  square.addChild(new Bitmap(new BitmapData(20, 20, false, 0)));
  square.x = 5 + 25 * i;
  square.addEventListener(MouseEvent.CLICK, myFunctions[i]);
  stage.addChild(square);
}

動的オブジェクトを介したプロパティなし、カスタムクラスなし、ルーズ関数なし、スコープオーバーラップなし。ロジックが期待するとおり:引数を単に渡すだけです。

そして、すべてのリスナーを適切に削除するには、後でこのようにできます:

for (i = 0; i < myFunctions.length; i++) {
  square = stage.getChildByName("sqr" + i) as Sprite;
  trace("Before (#" + (i+1) + "): " + square.hasEventListener(MouseEvent.CLICK));
  square.removeEventListener(MouseEvent.CLICK, myFunctions[i]);
  trace("After (#" + (i+1) + "): " + square.hasEventListener(MouseEvent.CLICK));
  stage.removeChild(square);
}

私見これはそれを行うための最もシンプルでありながら最も確実な方法です。

88
user1837285

ここに例があります

var s1:Boolean = true;

station1.addEventListener(MouseEvent.ROLL_OVER, function(e:MouseEvent) : void { spread(e, s1) });
station1.addEventListener(MouseEvent.ROLL_OUT, function(e:MouseEvent) : void { collapse(e, s1) });


function spread(event:MouseEvent ,bb:Boolean):void {
 if(bb != true) event.currentTarget.gotoAndPlay(2);
}

function collapse(event:MouseEvent,bb:Boolean ):void {
    if(bb != true) event.currentTarget.gotoAndPlay(18);
}
6
xbraim

パラメーターをイベントハンドラーに直接渡すことはできません。

最適な近似は、関数ファクトリー方式IMOを使用することです

//-編集:パラメーター化されたハンドラーを維持するために別のクラスを作成しました。 https://Gist.github.com/4124722 を参照してください

使い方は簡単です:

    public class Usage extends Sprite{

        private var _handlers : ParameterizedHandlerContainer;

        public function ParameterizedEvents()
        {

            _handlers = new ParameterizedHandlerContainer();
            _handlers.registerHandler( this, Event.ADDED_TO_STAGE, someMethod, 3000 );
        }

        private function someMethod( event : Event, amount : uint ):void
        {
            trace( this, 'someMethod', amount );
            _handlers.destroyHandler( arguments.callee, event );
        }


    }

これで、パラメーター化されたハンドラーをParameterizedHandlerContainer#registerHandlerで登録し、ParameterizedHandlerContainer#destroyHandlerで破棄できます。

必要な数のパラメーターを渡すことができますが、コールバックメソッドの最初のパラメーターは、Event型または子孫型でなければなりません。

1
Creynders

AS3-Signalsを使用しないのはなぜですか?パラメータを渡すのは次のように簡単です:

import org.osflash.signals.Signal;

public class AlarmClock 
{
    public var alarm:Signal;

    public function AlarmClock() 
    {
        alarm = new Signal(String);
    }

    public function ring():void
    {
        alarm.dispatch("9 AM");
    }
}
0
Varnius

Creyndersの例を少し変更します。

    protected function createHandler(handler:Function, ...args):Function
    {
        var h:Function = function(e:Event = null):void
        {
            trace(args);

            if (handler != null)
                handler(e);
        }
        return h;
    }

    sm.addEventListener(StyleEvent.STYLE_CHANGE, 
            createHandler(updateStyle, 1,true,"Lorem",["a","b","c"]), 
            false, 0, true);

このように、元のイベントハンドラを使用して、それに「引数」を挿入できます。

また、ハンドラに次の署名を持たせることもできます。

protected function myHandler(e:Event, ...args):void;

その後、引数をターゲットハンドラに直接渡すことができます。

    protected function createHandler(handler:Function, ...args):Function
    {
        var h:Function = function(e:Event = null):void
        {
            //trace(args);

            if (handler != null)
            {
                args.unshift(e);
                handler.apply(this, args);
            }
        }
        return h;
    }

宜しくお願いします