Есть ли способ удалить неизвестные прослушиватели событий из объектов?

Я хочу иметь кнопку многократного использования, которую можно зарегистрировать для одного из множества различных обратных вызовов, определяемых внешним источником. Когда установлен новый обратный вызов, я хочу удалить старый. Я также хочу иметь возможность очистить обратный вызов извне в любое время.

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» в обратном вызове, но я не хочу, чтобы эта функциональность была привязана к обратному вызову - например, я мог бы захотеть дважды щелкнуть кнопку.

Предложения?


person Community    schedule 08.10.2008    source источник


Ответы (7)


Я предполагаю, что вам нужна только одна функция обратного вызова в любой момент времени. Если это так, то почему бы не иметь единственную функцию обратного вызова, связанную с событием щелчка на кнопке, которая сама вызывала функцию, и чтобы эта функция была настраиваемой ...

<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 и обернуть все это внутри него.

person Simon    schedule 08.10.2008
comment
Попался. Знаете, мне кажется, я начал это делать и отказался от этого по какой-то причине, которую не помню. В любом случае, ваше предположение верно, и это хороший способ сделать то, что я задумал. Спасибо. - person ; 08.10.2008
comment
Отлично, рад, что смог помочь. Если вы отметите это как ответ, я тоже получу несколько очков репутации :-) - person Simon; 09.10.2008

Нет. Вам необходимо сохранить ссылку на слушателя, чтобы удалить его. Если вы заранее не сохраните ссылку на функцию прослушивателя, не существует документированного общедоступного метода для получения такой ссылки из 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. По сути, они дают вам возможность узнать, «изменился ли» поток событий, но они не дают вам возможности что-либо сделать с этой информацией!

person Triynko    schedule 20.11.2009

Сохраните слушателя как опору. Когда добавляется другое событие, проверьте, существует ли прослушиватель, и если он существует, вызовите removeEventListener.

Alternatively, override the addEventListener method of you button. When addEventListener is called, store the closure before adding it to the event in a Dictionary object. When addEventListener is called again, remove it:


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

};

person geraldalewis    schedule 13.10.2008
comment
Ни один из этих вариантов не выполняет то, что задает вопрос, а именно извлекает прослушиватель событий UNKOWN из существующего объекта EventDispatcher. Эти методы просто сохраняют ИЗВЕСТНЫЙ слушатель перед его добавлением к объекту. Если прослушиватель неизвестен и сохранен до того, как он добавлен к объекту, то получить его позже невозможно. - person Triynko; 20.11.2009
comment
Но они действительно предлагают способы сделать неизвестных слушателей событий известными, что является еще одним подходом к решению проблемы. - person voidstate; 26.02.2010

Для этой цели я написал подкласс под названием EventCurb, см. Мой блог здесь или вставьте его ниже.

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");
         }
      }
   }
}
person Thomas Thorstensson    schedule 01.08.2010

Мне нравится использовать динамический глобальный класс и добавлять краткую ссылку на встроенную функцию прослушивателя. Предполагается, что вам нравится иметь функцию слушателя в методе addEventListener, как это делаю я. Таким образом, вы можете использовать removeEventListener внутри addEventListener :)

Попробуйте это:

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. Благодаря этому вы можете динамически добавлять свойства во время выполнения.

person Jonathan Dumaine    schedule 15.02.2009

Следующее не решает фундаментальную проблему удаления неизвестных прослушивателей событий, но если вам нужно отключить все события, связанные с мышью, включая неизвестные, просто используйте: mouseEnabled = false для своей цели события.

Здесь еще полезные материалы: http://www.oughtprocessinteractive.com/blog/the-power-and-genius-of-mousechildren-and-mouseenabled

person Daniel Szmulewicz    schedule 04.10.2010

Класс K2xL EventManager http://k2xl.com/wordpress/2008/07/02/as3-eventmanager-class-removealllisteners/

person K2xL    schedule 18.08.2011