【问题标题】:How to create custom MouseEvent.CLICK event in AS3 (pass parameters to function)?如何在 AS3 中创建自定义 MouseEvent.CLICK 事件(将参数传递给函数)?
【发布时间】:2010-09-30 09:42:42
【问题描述】:

这个问题不仅仅与 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 事件并将参数传递给它时,我找不到触发自定义事件的方法。现在不知道是不是正确答案。

我应该怎么做?

【问题讨论】:

  • 对于其他研究此问题的人,我在此处添加了一个关于将参数传递给函数的快速代码示例:rocketwidget.com/…

标签: apache-flex flash actionscript-3 events closures


【解决方案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);
  }
}

此技术可以与您可以使用 addEventListener 的任何 AS3 事件类型相关。

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

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)感到失望,迫使您为每种类型构建一个完整的自定义扩展对象/事件。

此解决方案处理每个“可收听”的对象和事件。 this answer 有更多的细节和例子。

【讨论】:

    【解决方案2】:

    非常感谢这个有用的提示,这种技术比类解释更容易理解。

    对我来说,我刚刚开始使用这种技术来解决计时器数组和视口数组之间的链接关系,并通过频繁更改其中的文本来更新状态,通过将 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"); 
       }
    }
    

    【讨论】:

      【解决方案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 中的动态函数构造。

      【讨论】:

        【解决方案4】:

        您确实需要扩展事件类以使用额外参数创建自己的事件。将函数放在 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 组件中,您将触发自定义事件并侦听该事件而不是 Click 事件。

        【讨论】:

        • 为什么匿名函数可能是导致内存泄漏的原因?
        • 你没有对该函数的引用,如果你不小心的话,它与 eventListener 默认为 false 中的 weakReference 相结合会导致内存泄漏。被认为是不好的做法 + 任何 9.115 或更低版本的 flashplayer 都有一个导致匿名函数内存泄漏的错误。
        • 这仍然不能解释当 MouseEvent.CLICK 事件发生时我将如何触发/调度这个自定义事件。我错过了什么吗?
        • 你的问题有点模糊,你问的是自定义事件,这就是我试图解释的。有了它们,您可以添加任何您想要的参数。如果您希望在用户单击您的组件时触发自定义事件,只需在组件的单​​击处理程序中使用 dispatchEvent 代码即可。
        【解决方案5】:

        您可以通过将处理程序从提供变量闭包的函数中取出来完成此操作,如下所示:

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

        另外,至于为什么这会创建一个新的闭包,您可能需要查看this similar question 的已接受答案中的解释。

        【讨论】:

          【解决方案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());
          }
          

          应该这样做!祝你好运。

          【讨论】:

            猜你喜欢
            • 2010-10-24
            • 1970-01-01
            • 1970-01-01
            • 2010-10-15
            • 1970-01-01
            • 1970-01-01
            • 2010-12-24
            • 1970-01-01
            • 2019-09-01
            相关资源
            最近更新 更多