Is there a way to remove unknown event listeners from objects?

I want to have a reusable button that can be registered for one of many different callbacks defined by an external source. When a new callback is set, I want to delete the old one. I also want to be able to clear the callback from the outside 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 have seen suggestions for using "arguments.callee" in a callback, but I do not want this function to be bound to a callback - for example, I might want to double-click the button.

Suggestions?

+6
actionscript-3
source share
8 answers

I assume that you want only one callback function at any given time. If so, then why not have one callback function associated with the click event on the button that called the function itself, and this function can be set ...

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

The user of your control where your button is located will set onClickFunction with the corresponding function. In fact, you can install it as often as you like.

If you want to go one step further, you can subclass the AS3 Button class and wrap it all inside it.

+8
source share

Not. To remove it, you need to specify a link to the listener. If you do not save the link to the listener function in advance, there is no documented public method to get such a link from 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 ways to tell you if a registered listener is registered, or if one of his parents has a registered listener, but none of these methods returns a list of registered listeners.

Now, please head to Adobe to write such a useless API. Basically, they give you the opportunity to find out "the flow of events has changed, but they do not give you anything to do with this information!

+4
source share

Keep the listener as a prop. When another event is added, check if a listener exists, and if so, call removeEventListener.

Alternatively, override the addEventListener method for you. When addEventListener is called, store the closure before adding it to the event in the Dictionary object. When addEventListener is called again, delete 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 ); }; 

code>
+3
source share

I wrote a subclass of EventCurb for this purpose, see here 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"); } } } } 
+2
source share

Something I like to do is use a dynamic global class and quickly add a link to the inline listener function. It is assumed that you like to have a listener function in the addEventListener method, like me. So you can use removeEventListener inside addEventListener :)

Try the following:

 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 make some text fields var t:TextField = new TextField(); t.text = String(i); tx = stage.stageWidth*Math.random(); ty = 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)); }); } } 

}

//look here! This is an important bit dynamic class Global {}

The secret is the Global dynamic class. Because of this, you can dynamically add properties at run time.

+1
source share
 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 } } 
0
source share

Below is the main problem of deleting unknown event listeners, but if you need to disable all mouse-related events, including unknown ones, just use: mouseEnabled = false for your target purpose.

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

0
source share
0
source share

All Articles