如何在AS3中向事件侦听器传递参数?有简单的方法吗?

25

预期/伪例子:

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)在函数中。

    并非所有类都是动态的。例如,在Sprite上不起作用。

  • 扩展对象的类以添加属性或仅使其变为动态。

    您必须每次创建一个全新的自定义类。

  • 使用匿名函数作为事件处理程序。

    没有参考(而且很丑)。为了释放资源而删除侦听器,你被迫使用arguments.callee从函数内部执行它。

  • 在事件处理程序内调用另一个函数,使用参数。

    那么参数在事件处理程序调用中的位置在哪里?

  • 将事件处理程序保留在与参数相同的范围内。

    破坏完整的语义混乱。

  • 封装事件处理程序定义和addEventListener调用在接收目标和参数的函数中。

    它可以混合范围,但是很接近。不过你必须小心。

……还有许多其他建议的解决方法。

我只想将参数传递给事件处理程序,以便我可以像任何普通函数一样在其中使用它!

我要求太多了吗?



1
我编辑了我的答案,使用一个单独的类来处理处理程序的创建、存储和注册。 - Creynders
5个回答

93

开箱即用:只需要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);
}

在我看来,这是最简单但也是最可靠的方法。


6
当然可以。实际上,它是“以安全为导向”的:该函数的寿命将持续到您获得其返回的侦听器,其引用可以保存在变量中,以确保稍后进行删除。您对所有内容都有控制权。 - user1837285

8

这里是一个例子

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);
}

2
为什么不使用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

直接将参数传递给事件处理程序是不可能的。

在我看来,最好的近似方法是使用函数工厂方法。

//--编辑: 我创建了一个单独的类来维护参数化处理程序,请参见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销毁它们。
您可以传递任意数量的参数,但回调方法的第一个参数必须是事件类型或其子类。

修改了我的回答,进行了抽象,并添加了删除处理程序的可能性。 - Creynders

-1

我会稍微修改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;
    }

此致敬礼


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接