有没有办法从对象中删除未知的事件侦听器?

时间:2008-10-08 18:18:00

标签: actionscript-3

我希望有一个可重复使用的按钮,可以注册许多不同的回调之一,由外部源决定。设置新的回调时,我想删除旧回调。我也希望能够随时在外部清除回调。

public function registerButtonCallback(function:Function):void
{
  clearButtonCallback();

  button.addEventListener(MouseEvent.CLICK, function, false, 0, true);
}

public function clearButtonCallback():void
{
  if (button.hasEventListener(MouseEvent.CLICK) == true)
  {
    // do something to remove that listener
  }
}

我在这里看到过在回调中使用“arguments.callee”的建议,但我不希望将该功能与回调相关联 - 例如,我可能希望能够单击该按钮两次

建议?

8 个答案:

答案 0 :(得分:8)

我假设您在任何给定时间只需要一个回调函数。如果是这种情况那么为什么没有一个回调函数与按钮上的click事件相关联,该按钮本身称为函数并且该函数可以设置...

<mx:Button click="doCallback()" .../>

public var onClickFunction:Function = null;
private function doCallback():void
{
    if (onClickFunction != null)
    {
        onClickFunction(); // optionally you can pass some parameters in here if you match the signature of your callback
    }
}

您的控件的消费者将使用适当的函数设置onClickFunction。事实上,你可以随意设置它。

如果你想更进一步,你可以继承AS3 Button类并将其全部包装在其中。

答案 1 :(得分:4)

没有。您需要保持对侦听器的引用才能将其删除。除非您事先存储对侦听器函数的引用,否则没有可用于从EventDispatcher检索此类引用的文档公共方法。

addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
dispatchEvent(event:Event):Boolean
hasEventListener(type:String):Boolean
removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
willTrigger(type:String):Boolean 

正如您所看到的,有两种方法可以告诉您某种类型的事件是否已注册侦听器,或者其父节点之一是否已注册侦听器,但这些方法实际上都没有返回已注册侦听器的列表。

现在请骚扰Adobe编写这样一个无用的API。基本上,它们使您能够知道“是否”事件流已经改变,但它们无法对该信息做任何事情!

答案 2 :(得分:3)

将侦听器存储为道具。添加另一个事件时,检查侦听器是否存在,如果存在,则调用removeEventListener。

或者,覆盖您按钮的addEventListener方法。调用addEventListener时,在将闭包添加到Dictionary对象中的事件之前存储闭包。再次调用addEventListener时,将其删除:


var listeners:Dictionary = new Dictionary();

override public function addEventListener( type : String, listener : Function, useCapture : Boolean = false, priority : int = 0, useWeakReference : Boolean = false) : void {

  if( listeners[ type ] ) {

     if( listeners[ type ] [ useCapture ] {

        //snip... etc: check for existence of the listener

        removeEventListener( type, listeners[ type ] [ useCapture ], useCapture );

        listeners[ type ] [ useCapture ] = null;

        //clean up: if no listeners of this type exist, remove the dictionary key for the type, etc...

     }

  }

  listeners[ type ] [ useCapture ] = listener;

  super.addEventListener( type, listener, useCapture, priority, useWeakReference );

};

答案 3 :(得分:2)

我为此编写了一个名为EventCurb的子类,请在此处查看我的blog或粘贴在下面。

package
{
   import flash.events.EventDispatcher;
   import flash.utils.Dictionary;
   /**
    * ...
    * @author Thomas James Thorstensson
    * @version 1.0.1
    */
   public class EventCurb extends EventDispatcher
   {
      private static var instance:EventCurb= new EventCurb();
      private var objDict:Dictionary = new Dictionary(true);
      private var _listener:Function;
      private var objArr:Array;
      private var obj:Object;

      public function EventCurb() {
         if( instance ) throw new Error( "Singleton and can only be accessed through Singleton.getInstance()" );
      }

      public static function getInstance():EventCurb {
         return instance;
      }

      override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
      {
         super.addEventListener(type, listener, useCapture, priority, useWeakReference);
      }

      override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
      {
         super.removeEventListener(type, listener, useCapture);
      }

      public function addListener(o:EventDispatcher, type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
         // the object as key for an array of its event types
         if (objDict[o] == null)  objArr = objDict[o] = [];
         for (var i:int = 0; i <  objArr.length; i++) {
            if ( objArr[i].type == type)
            trace ("_______object already has this listener not adding!")
            return
         }
         obj = { type:type, listener:listener }
         objArr.push(obj);
         o.addEventListener(type, listener, useCapture, priority, useWeakReference);
      }

      public function removeListener(o:EventDispatcher, type:String, listener:Function, useCapture:Boolean = false):void {
         // if the object has listeners (ie exists in dictionary)
         if (objDict[o] as Array !== null) {
            var tmpArr:Array = [];
            tmpArr = objDict[o] as Array;
            for (var i:int = 0; i < tmpArr.length; i++) {
               if (tmpArr[i].type == type) objArr.splice(i);
            }

            o.removeEventListener(type, listener, useCapture);
            if (tmpArr.length == 0) {
               delete objDict[o]
            }
         }else {
            trace("_______object has no listeners");
         }
      }

      /**
       * If object has listeners, returns an Array which can be accessed
       * as array[index].type,array[index].listeners
       * @param   o
       * @return Array
       */
      public function getListeners(o:EventDispatcher):Array{
         if (objDict[o] as Array !== null) {
            var tmpArr:Array = [];
            tmpArr = objDict[o] as Array;
            // forget trying to trace out the function name we use the function literal...
            for (var i:int = 0; i < tmpArr.length; i++) {
               trace("_______object " + o + " has event types: " + tmpArr[i].type +" with listener: " + tmpArr[i].listener);
            }
            return tmpArr

         }else {
            trace("_______object has no listeners");
            return null
         }

      }

      public function removeAllListeners(o:EventDispatcher, cap:Boolean = false):void {
         if (objDict[o] as Array !== null) {
            var tmpArr:Array = [];
            tmpArr = objDict[o] as Array;
            for (var i:int = 0; i < tmpArr.length; i++) {
               o.removeEventListener(tmpArr[i].type, tmpArr[i].listener, cap);
            }
            for (var p:int = 0; p < tmpArr.length; p++) {
               objArr.splice(p);
            }

            if (tmpArr.length == 0) {
               delete objDict[o]
            }
         }else {
            trace("_______object has no listeners");
         }
      }
   }
}

答案 4 :(得分:1)

我喜欢做的事情是使用动态Global类并添加对内联侦听器函数的快速引用。这假设你喜欢像我一样在addEventListener方法中使用监听器函数。这样,您可以在addEventListener :)中使用removeEventListener

试试这个:

package {

import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;

[SWF(width="750", height="400", backgroundColor="0xcdcdcd")]
public class TestProject extends Sprite
{   
    public function TestProject()
    {
        addEventListener(Event.ADDED_TO_STAGE, Global['addStageEvent'] = function():void {
            var i:uint = 0;
            //How about an eventlistener inside an eventListener?
            addEventListener(Event.ENTER_FRAME, Global['someEvent'] = function():void {
                //Let's make some text fields
                var t:TextField = new TextField();
                    t.text = String(i);
                    t.x = stage.stageWidth*Math.random();
                    t.y = stage.stageHeight*Math.random();
                addChild(t);
                i++;
                trace(i);
                //How many text fields to we want?
                if(i >= 50) {
                    //Time to stop making textFields
                    removeEventListener(Event.ENTER_FRAME, Global['someEvent']);
                    //make sure we don't have any event listeners
                    trace("hasEventListener(Event.ENTER_FRAME) = "+hasEventListener(Event.ENTER_FRAME));    
                }
            });

            //Get rid of the listener
            removeEventListener(Event.ADDED_TO_STAGE, Global['addStageEvent']);
            trace('hasEventListener(Event.ADDED_TO_STAGE) = '+hasEventListener(Event.ADDED_TO_STAGE));

        });
    }

}   

}

//看起来在这里!这是重要的一点 动态类Global {}

秘诀是动态类Global。有了它,您可以在运行时动态添加属性。

答案 5 :(得分:0)

private function callFunction(function:Function):void
{
     checkObject();
     obj.addEventListener(MouseEvent.CLICK,function);
}

private function checkObject():void
{
    if(obj.hasEventListener(MouseEvent.CLICK))
   {
      //here remove that objects
   }
}

答案 6 :(得分:0)

以下内容并未解决删除未知事件侦听器的基本问题,但如果您需要禁用所有与鼠标相关的事件(包括未知事件),请使用: 您的事件目标上的mouseEnabled = false。

这里有更好的东西: http://www.thoughtprocessinteractive.com/blog/the-power-and-genius-of-mousechildren-and-mouseenabled

答案 7 :(得分:0)