如何在AS3中创建自定义MouseEvent.CLICK事件(传递参数到函数)?

7
这个问题不仅涉及到MouseEvent.CLICK事件类型,而是所有在AS3中已经存在的事件类型。我阅读了很多关于自定义事件的文章,但直到现在我还无法弄清楚如何做我想做的事情。我将尝试解释一下,希望你能理解:
这是我的情况的一个例子:
for(var i:Number; i < 10; i++){
    var someVar = i;
     myClips[i].addEventListener(MouseEvent.CLICK, doSomething);
}

function doSomething(e:MouseEvent){ /* */ }

但我希望能将someVar作为参数传递给doSomething函数。所以我尝试了以下代码:

for(var i:Number; i < 10; i++){

    var someVar = i;
    myClips[i].addEventListener(MouseEvent.CLICK, function(){
        doSomething(someVar);
    });
}

function doSomething(index){ trace(index); }

这种方法有所作用,但并不符合我的期望。由于函数闭包的原因,当MouseEvent.CLICK事件实际触发时,for循环已经结束,someVar保留的是最后一个值——在本例中是数字9。因此,每个电影剪辑中的点击都会调用doSomething,并将9作为参数传递。这不是我想要的结果。
我认为创建自定义事件应该可以解决问题,但是我找不到一种在MouseEvent.CLICK事件触发时触发自定义事件并将参数传递给它的方法。现在我不知道这是否是正确的答案。
我该怎么做?如何解决?

对于其他人正在研究此问题的人,我在这里添加了一个快速的代码示例,演示如何将参数传递给函数:http://rocketwidget.com/how-to-pass-additional-parameters-to-an-event-listener-in-flex/ - Jason Towne
6个回答

7
您真的需要扩展事件类来创建带有额外参数的自定义事件。将函数放置在addEventListener(匿名函数)中会导致内存泄漏,这是不好的。 请看下面的例子。
import flash.events.Event;

//custom event class to enable the passing of strings along with the actual event
public class TestEvent extends Event
{
    public static const TYPE1 :String = "type1";
    public static const TYPE2 :String = "type2";
    public static const TYPE3 :String = "type3";

    public var parameterText:String;

    public function TestEvent (type:String, searchText:String)
    {
        this.parameterText = searchText;
        super(type);
    }

}

当您创建一个新的事件,比如

dispatchEvent(new TestEvent(TestEvent.TYPE1, 'thisIsTheParameterText'))" ;

你可以像这样监听该事件。
someComponent.addEventListener(TestEvent.TYPE1, listenerFunction, true , 0, true);

在函数“listenerFunction”内,event.parameterText将包含您的参数。

因此,在您的myClips组件内,您会触发自定义事件并侦听该事件,而不是单击事件。


匿名函数为什么很可能是内存泄漏的罪魁祸首? - fenomas
你没有对该函数的引用,并且事件侦听器中的weakReference默认为false,如果不小心使用会导致内存泄漏。这被认为是不良行为 + 任何FlashPlayer 9.115或更低版本都存在一个缺陷,会导致匿名函数的内存泄漏。 - kenneth
这仍然没有解释当MouseEvent.CLICK事件发生时我如何触发/分派这个自定义事件。我有什么遗漏吗? - lbrandao
你的问题有点模糊,你问到了自定义事件,这就是我试图解释的。使用它们,您可以添加任何参数。如果您希望在用户单击组件时触发自定义事件,只需在组件的单击处理程序中使用dispatchEvent代码即可。 - kenneth

6

如果不了解您的应用程序更多信息,似乎更应该使用目标来传递参数,或者扩展MouseEvent。前者更符合常规实践。例如,如果在“剪辑”对象(无论它是什么)上公开了一个整数公共属性:

public class MyClip
{
   public var myPublicProperty:int;

   public function MyClip() { //... }
}

for (var i:int = 0; i < 10; i++)
{
   myClips[i].myPublicProperty = i;
   myClips[i].addEventListener(MouseEvent.CLICK, doSomething);
}

然后,在您的事件监听器中,您可以使用事件的target或currentTarget属性之一来检索该属性(在您的情况下可能是currentTarget):

function doSomething(event:MouseEvent):void
{
   trace(event.currentTarget.myPublicProperty.toString());
}

那应该就可以了!祝你好运。

3
    private function myCallbackFunction(e:Event, parameter:String):void
    {
         //voila, here's your parameter
    }

    private function addArguments(method:Function, additionalArguments:Array):Function 
    {
         return function(event:Event):void {method.apply(null, [event].concat(additionalArguments));}
    }

    var parameter:String = "A sentence I want to pass along";
    movieClip.addEventListener(Event.COMPLETE, addArguments(myCallbackFunction, [parameter] ) );

利用AS3中的动态函数构造功能。

2
通过将处理程序从提供变量闭包的函数中取出,您可以实现此目标,例如:
for (var i=0; i<5; i++) {
    myClips[i].addEventListener( MouseEvent.CLICK, getHandler(i) );
}

function getHandler(i) {
    return function( e:MouseEvent ) {
        test(i);
    }
}

function test( j ) {
    trace("foo "+j);
}

此外,关于为什么这会创建一个新的闭包,您可以查看此类似问题中已接受答案的解释。


1
非常感谢这些有用的技巧,这种方法比类的解释更易于理解。
对我来说,我刚开始使用这种技术来解决计时器数组和视口数组之间的链接关系,并通过在其内部频繁更改文本来更新状态,通过传递ID与计时器事件一起。
像这样:
    var view:Object=[];

    for(var i:uint=0;i<Camera.names.length;i++){

    view[i]=getChildByName("Cam"+i);

    //_________ Add Text _________
    var tf:TextField = new TextField();
    tf.autoSize = TextFieldAutoSize.LEFT;
    tf.textColor=0xffffff;
    view[i].tf=view[i].addChild(tf);

    //_________ Add Timer _________
    var t:Timer = new Timer(1000);
    view[i].timer=t;
    view[i].timer.start();
    view[i].timer.addEventListener(TimerEvent.TIMER, addArg(i)); 
}

function addArg(adi:uint):Function {
    return function(event:TimerEvent):void {
        updatecamstatus(adi);
    }
}
function updatecamstatus(vH:uint):void {
   with (view[vH]){
    tf.text="Cam" + vH + "\n";
    tf.appendText("activityLevel: " + videosource.activityLevel + "\n"); 
    tf.appendText("motionLevel: " + videosource.motionLevel + "\n"); 
   }
}

1

我看到你的主要目标并不是创建一个自定义的MouseEvent.CLICK,而是将参数传递给函数。你不需要复杂地创建或扩展任何东西。有一种简单且没有闭包问题的方法可以实现。

只需像这样编写您的函数:

function doSomething(index:Number):Function {
  return function(e:MouseEvent):void {
    // Use "e" and "index" here. They'll be unique for each addEventListener()
    trace(index);
  }
}

这种技术可以与任何AS3事件类型相关,您可以在其上使用addEventListener。

现在您可以像这样添加它:

var functionsDoSomething:Object;

for (var i:Number = 0; i < 10; i++) {
  var someVar:Number = i;
  functionsDoSomething[i] = doSomething(someVar);
  myClips[i].addEventListener(MouseEvent.CLICK, functionsDoSomething[i]);
}

doSomething(someVar) 可以直接用于 addEventListener(),但最好将其保存在变量中,因为您可以以相同的方式稍后删除它:

for (i = 0; i < 10; i++) {
  myClips[i].removeEventListener(MouseEvent.CLICK, functionsDoSomething[i]);
}

常用的e.currentTarget.someCustomProperty适用于动态对象(即MovieClip),但对其他任何东西(即Sprite)都无法胜任,迫使您为每种类型构建一个完整的自定义扩展对象/事件。
这个解决方案处理了每个“可监听”的对象和事件。此答案有更多详细信息和示例。

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