What is 'this' in anonymous event handlers in AS3 - actionscript-3

Good day!
I noticed that in anonymous event handler this is referenced to global, not my class. I know that I can use outer referenced variables (because it creates closures), but how to get right this context?
Simple example:
_movieClipClassVariable = new MyCustomSpriteSubclass();
_movieClipClassVariable.addEventListener(MouseEvent.CLICK, function(event:Event):void {
trace(this); //gives 'global'
});
What about memory usage and garbage collection objects with anonymous handlers? Is declaring handlers as class method better?

I think most of the time declaring a method inside the class is the better choice, (because you don't have to think about the scope, to remove listeners, to not getting accidentally garbage collected, ...) however there are cases where an anonymous function might be a concise and clear approach. It depends.
For instance:
public class XYZ extends Sprite {
...
private function renderBtn() : void {
var btn : SomeButton = new SomeButton();
var ref : XYZ = this;
btn.addEventListener(MouseEvent.CLICK, function(event:Event):void {
trace("XYZ instance: " + ref);
btn.removeEventListener(MouseEvent.CLICK, arguments.callee);
removeChild(btn);
proceed();
});
addChild(btn);
}
private function proceed() : void {
...

Yes you should really declare a class to not end up in thin air.
The delegating technique you used there is mostly used in Javascript. As a flash developer I'd recommend naming the function and keeping it all together in a class.
As for the garbage collection, you'd need to removeEventListener with the exact same syntax than the addEvenListener in order to free it for garbage collection.
HTH

Instead of adding the functions declaration right inside the Event listener, declare the method in your class.
class myTestClass
{
private function listenForEvents():void
{
_movieClipClassVariable = new MyCustomSpriteSubclass();
_movieClipClassVariable.addEventListener(MouseEvent.CLICK, onClipClassClickHandler);
}
private function onClipClassClickHandler(event:MouseEvent):void
{
trace(this); // this is the instance of current "myTestClass"
}
}

More details on anonymous handlers:
http://www.ultrashock.com/forums/actionscript/anonymous-functions-remove-listener-121738.html

Related

Is there a way to remove listeners easier?

so, thereĀ“s a easy way to remove all childs from container like this
while (container.numChildren)
{
container.removeChildAt(0);
}
is there a way to remove stage.eventListener in plural (dont know if im using the word right), something like stage.eventListeners? something like that?
thanks!
No, there isn't. There is no mechanism, at least publicly available, that keeps track of event listeners. However, you could do it yourself.
Basically, you need to keep track of each listener that is added. The easiest way would be to extend the classes you want to do this on and override addEventListener().
private var listeners:Vector.<Object>;
override public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false ):void {
var obj:Object = {
type : type,
listener : listener
};
this.listeners.push( obj );
super.addEventListener( type, listener, useCapture, priority, useWeakReference );
}
override public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ):void {
var i:Number, l:Number;
l = this.listeners.length;
for ( i = 0; i < l; i++ ) {
if ( this.listeners[i].type == type && this.listeners[i].listener == listener ) {
this.listeners.splice( i, 1 );
break;
}
}
super.removeEventListener( type, listener, useCapture );
}
public function removeAllEventListeners():void {
var i:Number, l:Number;
l = this.listeners.length;
for ( i = 0; i < l; i++ ) {
super.removeEventListener( this.listeners[i].type, this.listeners[i].listener );
}
this.listeners.splice( 0, this.listeners.length ); //empties the vector
}
If extending a class is not an option for you (either because you are using Flash Pro with the timeline or if it's the stage you need to remove all event listeners from), you could manually add it to the Vector each time you add an event listener. It's not quite as simple as the way I described above, but it would still work.
Do note that you should probably add the useCapture property to the object that is being saved and check for that as well, but I have honestly never set that property to anything but default so I did not include it in my code
In FlashPlayer 11, DisplayObjectContainer supports a removeChildren function that removes all children. So you can replace your while loop with container.removeChildren()
As for removing all eventListeners, there is no built-in method for this. However, you could write something yourself to keep track of all listeners added to a particular object, allowing you to remove them all by looping over the collection.
Unfortunately the short answer is that it is not possible. Per default, event dispatchers do not publically keep track of what listeners are registered. So you cannot loop over some list and just remove them all.
You will have to remember the listeners you add yourself.
There is a very easy solution, Robert Penner's phenomenal Signals library. Here is an example:
import org.osflash.signal.Signal;
public class Rocket()
{
// Signals are used to dispatch messages to interested parties
public var enginesFiredSignal:Signal;
public function Rocket()
{
// You have to instantiate the signal for it to do anything
enginesFiredSignal = new Signal();
}
public function fireEngines():void
{
// Dispatch will notify any interested parties
enginesFiredSignal.dispatch();
}
public function dispose():void
{
// This is the magic call... it removes *all* interested parties.
// No need to iterate through them and removeEventListeners.
enginesFiredSignal.removeAll();
}
}
public class Academy()
{
public var rocket:Rocket;
public function Academy()
{
rocket = new Rocket();
// We are interested to know *when* the engines fire,
// so we "add" ourself to the Signal.
rocket.enginesFiredSignal.add( enginesFiredHandler );
// This call will force the Signal to do its thing
rocket.fireEngines();
// Disposing of the Rocket will automatically remove us from
// its Signal. No further event removal or maintenance required.
rocket.dispose();
}
public function enginesFiredHandler():void
{
trace( "engines were fired" );
}
}
You can go way beyond this to do parameter passing and all sorts of interesting things. I recommend you check out his blog.
http://flashblog.robertpenner.com
Here is a tutorial on how to get started with AS3 Signals:
http://www.youtube.com/watch?v=wB_sWJ5EgXI

Multiple dispatchHandler Actionscript 3

I want to dispatch an Event 2 times:
I have a MainClass, a SecondClass and a ThirdClass.
In the ThirdClass, there's a clickEvent. I dispatch it to the SecondClass:
this.addEventListener(MouseEvent.CLICK, clickHandler);
public static const CLICKED_HANDLER:String = "clickedHandler";
public function clickHandler(e:MouseEvent):void {
dispatchEvent(new Event(CLICKED_HANDLER));
}
I catch and throw it in the SecondClass:
object.addEventListener(ThirdClass.CLICKED_Handler, clickedEventListener);
public static const CLICKED_HANDLER:String = "clickedHandler";
public function clickedEventListener(e:Event):void {
dispatchEvent(new Event(CLICKED_HANDLER));
}
And this I catch in the MainClass:
object.addEventListener(SecondClass.CLICKED_HANDLER, clickedEventListener);
public function clickedEventListener(e:Event):void {
trace("click");
}
But it wouldn't work... What am I doing wrong?
And how could I get information about the object of the ThirdClass that's clicked?
(Normaly with 1 dispatchEvent, it's with:
var thirdClassObject:ThirdClass = e.currentTarget as ThirdClass;
in the clickHandler method, but how to do this with 2 dispatchEvents?)
So: I want to know in my MainClass which ThirdClass-object is clicked.
Thanks a lot!
When you dispatch an event, the target property references the dispatcher.
What you apparently need is passing a reference to third class along with the event to use in the event handler of main class.
You have several options to achieve this.
If a dispatcher instance is on the display list, use event bubbling to handle events in parent display objects. Refer to the second argument of Event constructor and Event.bubbles property. This way you can subscribe to any events of child objects in a parent, and check Event.target property in your event handler. Most of mouse events like MouseEvent.CLICK are bubbling by default. So you could just listen to them and check targets in main class.// inside MainClass
// notice: we are subscribing to MainClass instance
this.addEventListener(MouseEvent.CLICK, clickedEventListener);
public function clickedEventListener(e:MouseEvent):void {
trace(e.target); // the target is what was actually clicked
// you may also notice the difference between e.target and e.currentTarget
}
If you still want to use CLICKED_HANDLER event, you may do so as follows:// inside ThirdClass
dispatchEvent(new Event(CLICKED_HANDLER, true));
// ---------------------------------------^
Remember: this will only work for display list members.
See the Event flow article on adobe.com.
Create a custom event class and place reference there. (And you can also use event bubbling with custom event classes.) This way you will dispatch your event as// inside SecondClass
public function clickedEventListener(e:Event):void {
var customEvent:CustomEventClass = new CustomEventClass(CLICKED_HANDLER);
customEvent.customTarget = e.target;
dispatchEvent(customEvent);
}
// inside MainClass
public function clickedEventListener(e:CustomEventClass):void {
trace(e.customTarget );
}
First, define what do you mean by "it wouldn't work..."?
Does the SecondClass receive the event at all? If so, you may try this:
//SecondClass
public function clickedEventListener(e:Event):void {
dispatchEvent(e);
}
//MainClass
object.addEventListener(ThirdClass.CLICKED_HANDLER, clickedEventListener);

upcasting in actionscript 3

Hi I have a custom class that extends Sprite, that controls positioning on the stage. I would like the class that manages the stage to be able to take any sprite that is added to the stage and convert it to custom class type, ie wrap it so that the managing class can position a native sprite accordingly.
Does anyone know if it is possible to do something like the following
var managed:managedSprite = new managedSprite(nativeSprite);
where the managedSprite extends the Sprite class? I don't want to have to composite in a reference to the sprite if at all possible in order to prevent the overhead associated with the managedSprite already extending Sprite.
Is there anyway using reflection (perhaps the commons.reflection library)?
Thanks
You can add an event listener to the stage and use Event.ADDED to get a reference to any display object added anywhere in the display list.
Then simply type cast if the added item is a subclass of ManagedSprite (BTW, the convention is to start your class names with an uppercase letter):
stage.addEventListener (Event.ADDED, onAdded);
function onAdded( ev:Event ):void {
if (ev.target is ManagedSprite) {
var managed:ManagedSprite = ManagedSprite( ev.target );
doStuffWith( managed );
}
}
EDIT
I think I only just understood your question: You are not trying to do an actual type cast - which would require your class hierarchy to be already set up, i.e. you'd have to have extended the ManagedSprite class already - but to add functionality at runtime!
I would strongly discourage you from trying to do deep copies or such - it will be heavy on performance, depending on how many sprites you are going to add, and you will no longer have your compiler to help you prevent errors.
Rather, see if you can't favor composition over inheritance: Create a kind of "proxy" class for the sprite, let's call it ManagedSpriteProxy, which implements all the methods you would call on ManagedSprite, but forwards all the actual manipulations to its `managedSprite' property. Then use the event handler I outlined above to create the proxy objects and attach the respective sprites:
public class ManagedSpriteProxy {
private var _managedSprite:Sprite;
public function ManagedSpriteProxy( managedSprite:Sprite ) {
this.managedSprite = managedSprite;
}
public function get managedSprite():Sprite {
return _managedSprite;
}
public function set managedSprite( managedSprite : Sprite ):void {
_managedSprite = managedSprite;
setUpAnyHandlersOrWhatever();
}
private function setUpAnyHandlersOrWhatever():void {
// Many wonderful things happening
}
// Many more wonderful things happening via public API
}
// somewhere else
public class SpriteManager {
private var _managedSprites:Array = [];
public function SpriteManager() {
addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
}
private function onAddedToStage( ev:Event ):void {
removeEventListener( Event.ADDED_TO_STAGE );
stage.addEventListener( Event.ADDED, onAdded );
}
private function onAdded( ev:Event ):void {
if( ev.target is Sprite ) {
addWithProxy( ev.target as Sprite );
}
}
private function addWithProxy( sprite:Sprite ) : void {
var proxy:ManagedSpriteProxy = new ManagedSpriteProxy( sprite );
_managedSprites.push( proxy );
}
// Insert here whatever methods used to manage the sprites,
// all of them call the proxies instead of the sprites!
}
You may want to use the 'Decorator' pattern.
It seems a little bit complex at the first sight but it's quite easy to understand and use.
http://www.as3dp.com/2009/04/actionscript-30-easy-and-practical-decorator-design-pattern/

AS3 How to extends Event class without keeping any existing event types?

I would like to extends the Event class to add some events I am using in game.
But I don't want the new Event Class to have the old public static types..
For instance I don't want to have:
NewEventClass.ENTER_FRAME
How do you go about extending the Event class without getting the old types mixed in?
Is there any way to outsmart AS3 to leave out the uneeded types?
Or should I avoid creating a new Event type altogether and just add the new strings?
Extending Event is only really necessary if you want to add some extra properties to it, for example:
public class EnemyEvent extends Event
{
// Constants used to represent event type
public static const ENEMY_KILLED:String = "killed";
// Event properties
public var score:int = 0;
/**
* Constructor
* Retain Event behaviours
*/
public function EnemyEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
So that when you dispatch this event from an enemy you can go:
var evt:EnemyEvent = new EnemyEvent(EnemyEvent.ENEMY_KILLED);
evt.score = myScoreValue;
dispatchEvent(evt);
And then make use of the score property from the listening method within the game engine:
enemy.addEventListener(EnemyEvent.ENEMY_KILLED, _countKill);
function _countKill(e:EnemyEvent):void
{
gameTotalScore += e.score;
if(gameTotalScore > 100) getAchievement();
e.target.removeEventListener(e.type, _countKill); // <-- woudn't work without extending event either
}
If you just need to store constants to use in addEventListener(x, ..), new Event(x), etc then you can just make a class that holds these and doesn't have anything to do with Event:
public class CustomEvents
{
public static const MY_CUSTOM_EVENT:String = "myCustomEvent";
}
So that you can just use these as needed:
new Event(CustomEvents.MY_CUSTOM_EVENT);
addEventListener(CustomEvents.MY_CUSTOM_EVENT, _listener);
The former method is still preferable as it's tidier and more logical.
Additionally; your note about your custom event having constants such as ENTER_FRAME isn't the case anyway, because they are static and belong to Event. You'll get this error if you try access ENTER_FRAME through the example in your answer:
1119: Access of possibly undefined property ENTER_FRAME through a
reference with static type Class.

ActionScript Calling Private Functions By Changing Public Variables?

i've never tried to do this before, so my head a swimming a bit. i'd like to have a public boolean called enabled in myClass custom class. if it's called to be changed, how do i trigger a function from the change?
should i add an Event.CHANGE event listener to my variable? can i do that? or is there a more standard way?
We usually use properties for that.
Properties are just like public variables for the outside -- you can set instance.enabled = true; and so forth.. But you define properties as getters and/or setters functions for the class.
They are the perfect place for custom logic to be executed on value changes.
For example:
public class CustomClass {
private var _enabled:Boolean = false;
public function set enabled(value:Boolean):void {
trace('CustomClass.enabled is now', value);
this._enabled = value;
}
public function get enabled():Boolean {
trace('CustomClass.enabled was retrieved');
return this._enabled;
}
}
Note that they can't have the same name as your private variable and you don't need both of them defined. Actually, you don't even need a variable for a setter/getter. You could use them just like any function -- they just supply you with a different syntax.
For example:
var object:CustomClass = new CustomClass();
object.enabled = false;
if (object.enabled) {
...
}
They are great to expose a simple API, keeping you from rewriting outside code if the class' internals have to change.
AS3 Reference on getters and setters.