views:

11549

answers:

8

I want to have a reusable button which can be registered for one of many different callbacks, determined by an external source. When a new callback is set, I want to remove the old. I also want to be able to clear the callback externally at any time.

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

I've seen suggestions on here to use "arguments.callee" within the callback, but I don't want to have that functionality tied to the callback - for example, I might want to be able to click the button twice.

Suggestions?

+6  A: 

I am presuming that you want only one callback function at any given time. If that's teh case then why not have a single callback function associated with the click event on the button which itself called a function and have that function be settable...

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

A consumer of your control which houses your button would set the onClickFunction with the appropriate function. In fact you could set it as often as you liked.

If you wanted to go one step further you could subclass the AS3 Button class and wrap all of this inside it.

Simon
Gotcha. You know, I think I started to do that and decided against it for some reason which I don't remember. Either way, your presumption is correct and it's a good way to do what I've intended. Thanks.
Great, glad I could help. If you mark this as the answer I'll get a few rep points too :-)
Simon
+1  A: 
Neither of those options do what the question is asking, which is to retrieve an UNKOWN event listener from an existing EventDispatcher object. These methods are just storing a KNOWN listener before it's added to the object. If the listener is not known and stored before it's added to the object, then there is no way to retrieve it later.
Triynko
But they do offer ways to make the unknown event listeners known which is another way to approach the problem.
voidstate
+1  A: 

Something I like to do is use a dynamic Global class and add a quick reference to the listener function inline. This is presuming you like to have the listener function in the addEventListener method like I do. This way, you can use removeEventListener inside the addEventListener :)

Try this out:

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

  });
 }

}

}

// looky here! This is the important bit dynamic class Global {}

The secret is the dynamic class Global. With that you can dynamically add properties in at runtime.

Jonathan Dumaine
+2  A: 

No. You need to hold a reference to the listener in order to remove it. Unless you store a reference to the listener function in advance, there is no documented public method available to retrieve such a reference from an 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

As you can see, there are two methods to tell you whether a type of event has a listener registered or one of its parents has a listener registered, but none of those methods actually return a list of registered listeners.

Now please go harass Adobe for writing such a useless API. Basically, they give you the ability to know "whether" the event flow has changed, but they give you no way of doing anything with that information!

Triynko
A: 

I did a post on Tip and Tricks for event listeners this might help everyone http://www.almogdesign.net/blog/actionscript-3-event-listeners-tips-tricks/

Almog
+1  A: 

I written a subclass called EventCurb for that purpose, see my blog here or paste below.

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");
         }
      }
   }
}
Thomas Thorstensson
A: 

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

M.Raju
A: 

The following doesn't address the fundamental issue of removing unknown event listeners, but if what you need is disabling all mouse related events, including unknown ones, just use: mouseEnabled=false on your event target.

More good stuff here: http://www.thoughtprocessinteractive.com/blog/the-power-and-genius-of-mousechildren-and-mouseenabled

Daniel Szmulewicz