Reduce lifecycle validation calls in resizeable Flex LabelItemRenderer - actionscript-3

I've subclassed the LabelItemRenderer class to create an expandable renderer for a spark list in my mobile app.
When the user selects an item, the renderer's size increases, additional data is shown. The renderer basically looks like this (I've removed the parts that don't matter here, so this is basically pseudo code).
public class PositionsGridRenderer extends LabelItemRenderer
{
public function PositionsGridRenderer() {
super();
addEventListener(MouseEvent.CLICK, expandHandler);
}
protected override function createChildren():void {
super.createChildren();
_dg = new DataGroup();
_dg.visible = false;
addChild(_dg);
}
private function expandHandler(event:Event):void {
if(_gridVisible) {
if(!_detailClicked) {
_dg.visible = false;
_gridVisible = false;
}
_detailClicked = false;
} else {
_dg.visible = true;
_gridVisible = true;
}
}
public override function set data(value:Object):void {
if(!value) return;
super.data = value;
var pos:Position = data as Position;
label = pos.positionName;
_dg.dataProvider = pos.positionSymbols;
}
protected override function measure():void {
!_gridVisible ? measuredHeight = 30 : measuredHeight = 30 + getElementPreferredHeight(_dg);
this.height = measuredHeight;
}
protected override function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
setElementSize(labelDisplay, unscaledWidth, 30);
setElementPosition(labelDisplay, 10,10);
if(_gridVisible) {
setElementSize(_dg, unscaledWidth, getElementPreferredHeight(_dg));
setElementPosition(_dg, 0, 30);
} else {
setElementSize(_dg, unscaledWidth, 0);
}
invalidateSize();
}
}
}
Works as expected, I'm just wondering if there's a way to reduce the amount of validation calls this renderer does when I expand it.
If it is clicked to be expanded the layoutContents and measure functions are both called three times in the following order: layoutcontents -> measure, layoutcontens -> measure, layoutcontents -> measure.
I'd understand them being called once because I invalidate the size, but three times seems odd.
Does anyone know why this is happening, or maybe even how to prevent this from happening?

The real question was why is the component going through three full renderer cycles? After some disussion, this is what we came across:
The first time the invalidate cycle is triggered is when a mouse down, or possibly a touch begin event occurs. This puts the component into the hover state; which causes a visual change in the component.
The second time the invalidate cycle is triggered is when the item is selected. This puts the renderer in the down state; causing a different visual indicator to be drawn.
The third invalidate cycle is caused by the component's own code; when layoutContents() calls invalidatesize()

Related

Setters / Getters (AS3)

I have a menu page with one button, which leads to another page by adding a child and removing one. Very simple. From here l have a page which allows many buildings to be clicked, which opens a new page (a mini game page). I have done this with setters and getters as shown below:
Main.as (gets value from the getter in MainPage)
public function onEnterFrame(event:Event):void
{
//Swaping pages with a getter and setter
if (mainPage.stage && mainPage.getNextLevel() == 1)
{
addChild(miniGameOne);
removeChild(mainPage);
}
if (miniGameOne.stage && miniGameOne.getNextLevel() == 2)
{
addChild(mainPage);
removeChild(miniGameOne);
}
}
MainPage.as (with all the buildings)
public function onAddedToStage(event:Event):void
{
doctor.addEventListener(MouseEvent.CLICK, onDoctorClick);
}
public function onDoctorClick(event:MouseEvent):void
{
setNextLevel(1);
}
public function setNextLevel(passLevel:int)
{
nextLevel = passLevel;
}
public function getNextLevel():int
{
return nextLevel;
}
MiniGameOne.as
Here it says when the mini game is complete then set the page to 2, which is adding the MainPage.as and removing MiniGameOne.as
public function onEnterFrame(event:Event):void
{
healthbar.meter.scaleY = life/100;
if (life < 1)
{
life = 1;
//Make sure it isn't visiable
healthbar.meter.alpha = 0;
//New Function
gameComplete();
}
}
public function gameComplete()
{
//Level Complete, Set new Level
setNextLevel(2);
}
I have a problem, when l enter a page (clicking on a building) then return to the original page and click on the same building l can't open the same page again, can anyone explain what is happening here? Thanks.
That happens because you have defined and are checking nextLevelfor both MainPage and MiniGameOne classes. When you change the level in miniGameOne instance to 2 it will be 2 until you change it to some other number.
From that point you have:
mainPage.getNextLevel() == 1
and
miniGameOne.getNextLevel() == 2
so in your enterFrame() both conditions are true and your miniGameOne is indeen added to stage but in the same frame it is removed and you mainPage is added again.
As a quickest fix you can change the line:
if (miniGameOne.stage && miniGameOne.getNextLevel() == 2)
to
else if (miniGameOne.stage && miniGameOne.getNextLevel() == 2)
But this would probably leave your mini game only until next frame is reached so you need to reset nextLevel value when you are back in mainPage :
if (miniGameOne.stage && miniGameOne.getNextLevel() == 2){
addChild(mainPage);
removeChild(miniGameOne);
miniGameOne.setNextLevel(0);
}
However what you have here is very poor practice.
Fist of all, change only that parts of your program on frame event which you actually need to update on every frame, or you really don't have any other way to determine when it should be updated
Secondly, don't duplicate variables that store you app states.
There are many ways you can apporach this problem but I will show one that is probably closest to what you have already:
1 Define level state in your Main class, store your levels in array/vector/whatever, pass instance of main to your levels.
/**Current level**/
private var cl:uint = 0;
/**All levels veiews that could be shown.**/
private var levels:Array = [mainPage, miniGameOne];
public function Main() {
for each (var l in levels) l.main = this;
}
public function get level():uint { return cl; }
public function set level(l:uint) {
this.removeChild(levels[cl]);
this.addChild(levels[cl = l]);
}
2 In your level classes define setter for main instance, you now can change current level stored in main.
private var m:Main;
public function set main(v:Main):void { m = v; }
public function onDoctorClick(event:MouseEvent):void{
m.level = 1;
}
public function gameComplete(){
//Level Complete, Set new Level
m.level = 0;
}
3 All other code, especially on frame event, can be removed.
The solution may not be ideal but I hope it will give you some idea.

AS3 game inventory system structure

I'm not really expert on OOP and as3 now. I'm making inventory system that will handle this things:
*when player collide with an "item", it will get it and send to the inventory array, etc...
here is the Player class constructor
as you can see i write item's instance name manually: "silver_key"
public function Player(player:MovieClip,loot:MovieClip,place:MovieClip)
{
// constructor code
_player = player;
silver_key = loot;//the item
_inventory = new InventorySystem(place);//InventorySystem Class
_player.addEventListener(Event.ENTER_FRAME,on_enter_frame);
addEventListener(EventDemo.EVENT_DEFAULT,onEvent);//custom event listener
}
This is the functions relative to it:
private function on_enter_frame(e:Event):void
if (_player.hitTestObject(silver_key))//check collision between player and item
{
dispatchEvent(new EventDemo(EventDemo.EVENT_DEFAULT));
}
private function onEvent(e:EventDemo)
{
_inventory.getitem(_loot);//function use in InventorySystem for array.push
removeEventListener(EventDemo.EVENT_DEFAULT,onEvent);//removes the listener when the item was sent to array
}
now my question is how can i be able to create a lot of items without manually writing their instance name. Thanks.
ps: if you still can't understand please comment.
To put it simply:
object["string_name"] = value
For a more thorough explanation, read on...
You need explicit names for each item, but you want to dynamically define those items without knowledge of what it could be. You can store this in a number of ways, but a basic Array or Object should work fine.
It looks like you were creating a movieclip to represent the player. As a class, the instantiation of the player class could itself be that movieclip, but be aware that MovieClips carry a lot of timeline baggage that can slow you down. For a more succinct/lightweight player class, extend Sprite and write your own properties/methods.
For example, let's imagine this is what a Silver Key object looks like:
item:Object = {
"name":"Silver Key",
"count":1,
"weight":3,
"icon":"silver_key.jpg"
}
In one object, we can define a name property to be whatever we want that type of item to be, as well as other pertinent properties that describe that item. Now, all we need to do is keep a list of saved items in the player class itself.
Player Class
package {
public dynamic class Player extends Sprite {
import flash.events.*;
import flash.display.*;
public var inventory:Object = {};
public function Player() {
addEventListener(Event.ENTER_FRAME, tick);
}
public function tick(e:Event):void {
// ENTER_FRAME stuff
}
public function loot(item:Object) {
if (inventory.hasOwnProperty(item.name)) {
inventory.item.count++
trace("You now have " + inventory.item.count + " " + item.name + "'s!");
} else {
inventory[item.name] = item;
trace("You have picked up a " + item.name)
}
}
public function drop(item:Object) {
if (inventory.hasOwnProperty(item.name)) {
inventory.item.count--
if (inventory.item.count == 0) {
trace("You no longer have " + item.name);
delete(inventory[item.name]);
} else {
trace("You now have one less " + item.name + ".");
}
}
}
}
}
Example Implementation
var player:Player = new Player();
player.loot(item);
// traces "You picked up a Silver Key"
player.drop(item);
// traces "You no longer have Silver Key"
This is a simple way of doing it, and truthfully, you may want to expand on it with your own item & inventory classes, as well as data sanitization/checking to prevent a corrupted inventory.
Update: "More than likely, you'll create a prototype of each item."
Here's what one could look like.
package {
public class ItemDefinition extends Object {
public var name:String = "Unnamed Item";
public var count:int = 1;
public var weight:int = 0;
public var icon:String = "unnamed.jpg";
public function ItemDefinition(Properties:Object) {
// By passing in an object, we can define only the properties we want to change.
for (var Name:String in Properties) {
// Only property names that match will overwrite the defaults.
if (this.hasOwnProperty(Name)) {
this[Name] = Properties[Name]
}
}
}
}
}
What's cool about this, is that we can define any number of properties and in any order without fear of adding a bogus value that could mess us up later. Furthermore, if we want to add a new property to what defines a Item, just add it as a public var at the top.
Next, we might define all of our items once at the top of our document, using a new ItemDefinition object.
var itemDefinitions:Object = {
'Silver Key':new Item({name:"Silver Key", icon:"silver_key.jpg", weight:3}),
'Gold Key':new Item({weight:10, name:"Gold Key", icon:"gold_key.jpg"}),
'Iron Key':new Item({icon:"iron_key.jpg", weight:2, name:"Iron Key"})
}
function populateItems(itemName:String, count:int = 20) {
if (itemDefinitions.hasOwnProperty(itemName)) {
for (var i:int = 0; i < count; i++) {
var item:MovieClip = new MovieClip();
item.x = randomNumber(0, this.loaderInfo.width);
item.y = randomNumber(0, this.loaderInfo.height);
item["itemDefinition"] = itemName;
}
} else {
trace(itemName + " is an invalid item.");
}
}
function randomNumber(low:Number=0, high:Number=1):Number {
/* Returns a random number between the low and high values given. */
return Math.floor(Math.random() * (1+high-low)) + low;
}
As you can see, I've added a couple helper functions to spit out those extra items on screen (randomly). Obviously, we'd need to have an addChild, and image loading, but that's really something you should implement.
Finally, we'll revise our loot call to pass the itemDefinition...
player.loot(itemDefinitions[item.itemDefinition]);
removeChild(item);
Worlds of possibilities. You'll likely make something far more robust which addresses the needs of your game, so this is only one way you might do it.

how to detect children size change actionscript?

if i got a container,there were children display object in it.
the container's size was decided by the children's size.when the size change i need to do something to fit the new size.
since i didn't find the kind of event to detect it,i use ENTER_FRAME event,that's quite silly.
keep every last frame's size,and compare to current frame.maybe the size changed after you deal with the enter frame event,so in some cases,you might see the correct result in the next frame.
i don't think it's a good solution for a component.give me you ideas,thanks.
Well there is no standard solution for this. You could create a custom class which overrides the setters of width/height/x/y/scaleX/scaleY/scrollRect .. and maybe some other properties. The children should extend such a class.
I used a boolean to prevent its dispatched multiple times, after a frame the flag will be resetted.
override public function set width(value:Number):void
{
if (value !== super.width && !isNaN(Number(value)) this.dispatchResize();
super.width = value;
}
override public function set height(value:Number):void
{
if (value !== super.height && !isNaN(Number(value)) this.dispatchResize();
super.height = value;
}
override public function set scaleX(value:Number):void
{
if (value !== super.scaleX && !isNaN(Number(value)) this.dispatchResize();
super.scaleX = value;
}
override public function set scaleY(value:Number):void
{
if (value !== super.scaleY && !isNaN(Number(value)) this.dispatchResize();
super.scaleY = value;
}
private var _hasDispatchedResize:Boolean;
protected function dispatchResize():void
{
// do something
if (!this._hasDispatchedResize)
{
this.dispatchEvent(new Event(Event.RESIZE));
this._hasDispatchedResize = true;
this.addEventListener(Event.ENTER_FRAME, handleEnterFrameOnce);
}
}
private function handleEnterFrameOnce(event:Event):void
{
this.removeEventListener(Event.ENTER_FRAME, handleEnterFrameOnce);
this._hasDispatchedResize = false;
}
Now, in the container class you can listen to a Event.RESIZE of the children. Your not really sure if the value actually changed (in case of a frame change on a MovieClip), but in most cases this will work. I've added an extra check inside this setters before dispatching the resize. It depends on the case if this will suit your case.
What I would do is whatever function resizes the children, add your own dispatch event to it that would broadcast that a size change occurred.
//Never forget to import your classes.
import flash.events.EventDispatcher;
import flash.events.Event;
//Our custom listener listens for our custom event, then calls the function we
//want called when children are resized.
addEventListener("childResized", honeyIResizedTheChildren);
function resizingLikeFun(){
datClip.width++;//they be resizin, yo.
dispatchEvent(new Event("childResized"));//Our custom event
}
function honeyIResizedTheChildren(e:Event){
trace("Giant Cheerios");
//Whatever you want to do when the children are resized goes here.
}
I assume some things with that code, so let me know if this doesn't fully apply to you.

How to have the variable in a class updated when its value is set in another class?

I'm rather new with AS3, so forgive me if my question appears stupid.
Let me clarify what I want specifically: The Player goes on a mission, in which he would earn points when he catches some objects on his way, for example object A gives one point, object B gives two points, object C gives bonus points, etc... IF the Player hits some obstacle on his way, he fails the mission and he has to replay the game until he reaches the destination (end of level).
So what I want is that how I can store the points that the Player earned before he hits the obstacle and the earned points would be accumulated every time the Player has to replay the game until he reaches the end of the game level so that the total points the Player earned would be the sum of all the points, including those that he earned before he actually reaches the end of the game level.
My Class Player is the subclass of the class Box that belongs to the framework WCK. Is there a way for me to implement Dependency Injection properly to pass the variables to the other class in order to store them there?
Thank you in advance for your help!
ADDED: Here is what I implemented based on Amy's example with some adjustment to suit the setup of my game. It still doesn't work as the value of variable _totalEarnedPoints did not update the variable _earnedPoints in Class PlayerClient via EventDispatcher when I tried to retrieve it back in Class Player for accumulating all the points. I'm not sure what I did wrong or missed anything else? Can anyone please point out and tell me how to retrieve back the variable _totalEarnedPoints from Class PlayerClient?
CLASS Player:
package
{
public class Player extends Box
{
private var contacts:ContactList;
private var myLoader:SWFLoader;
private var mcTemp:MovieClip;
private var bonusPoints:int = 0;
private var onePoint:int = 0;
private var twoPoints:int = 0;
private var objectACount:int = 0;
private var objectBCount:int = 0;
private var objectCCount:int = 0;
private var replayCount:int = 0;
protected var _earnedPoints:int;
private var myPlayerClient:PlayerClient = new PlayerClient();
}
public function Player()
{
super();
}
public override function create():void
{
super.create();
listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, handleContact);
contacts = new ContactList();
contacts.listenTo(this);
}
public function handleContact(e:ContactEvent):void
{
//Detect collison with other static objects
var myObjectA:objectA = e.other.m_userData as objectA;
var myObjectB:objectB = e.other.m_userData as objectB;
var myObjectC:objectC = e.other.m_userData as objectC;
var myObstacle:obstacle = e.other.m_userData as obstacle;
if(myObjectC)
{
objectCCount++;
myObjectC.remove();
if (objectCCount > 0)
{
bonusPoints = bonusPoints + 5;
}
}
else if(myObjectA)
{
objectACount++;
myObjectA.remove();
if (objectACount > 0)
{
onePoint = onePoint + 1;
}
}
else if(myObjectB)
{
objectBCount++;
myObjectB.remove();
if (objectBCount > 0)
{
twoPoints = twoPoints + 2;
}
}
else if(myObstacle)
{
var myEarnedPoints:int = myPlayerClient.totalEarnedPoints;
_earnedPoints = bonusPoints + onePoint + twoPoints + myEarnedPoints;
dispatchEvent(new Event("EarnedPointChanged"));
myLoader = new SWFLoader("myMovie.swf",{container:swfHolder,alpha:0,onComplete:completeHandler});
//adds the loader content to the display list before raw content is loaded.
addChild(myLoader.content);
stop();
myLoader.load();
function completeHandler(e:LoaderEvent):void
{
replayCount++;
if (replayCount <= 1)
{
TweenMax.to(e.target.content, 1, {alpha:1});
mcTemp = myLoader.rawContent;
mcTemp.gotoAndPlay(1);
}
else if (replayCount >= 1)
{
mcTemp = myLoader.rawContent.stop();
myLoader.unload();
mcTemp = null;
}
}
myObstacle.remove();
}
}
public function get earnedPoints():int
{
return _earnedPoints;
}
}
CLASS PlayerClient
package
{
public dynamic class PlayerClient extends MovieClip
{
private var _totalEarnedPoints:int;
protected var _player:Player;
public function get player():Player
{
return _player;
}
public function set player(value:Player):void
{
if (value != _player)
{
if (_player)
{
//[prevent memory leaks][2]
_player.removeEventListener("EarnedPointChanged", updatePlayerScore);
//don't need this if we don't have a player
removeEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
_player = value;
if (_player)
{
//listen for changes
_player.addEventListener("EarnedPointChanged", updatePlayerScore);
//we added a listener to the new player, need to make sure is removed
addEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
}
}
protected function cleanUp(e:Event):void
{
_player.removeEventListener("EarnedPointChanged", updatePlayerScore);
}
protected function updatePlayerScore(e:Event):void
{
_totalEarnedPoints = _player.earnedPoints;
}
public function get totalEarnedPoints():int
{
return _totalEarnedPoints;
}
}
}
First, what you're doing is going to cause you a world of hurt unless your project is a banner or something that you know 100% that you will work on for 2 days and never look at again. See http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
What you need to realize is that your Points.pointsEarned looks like it will only ever change if you create a new instance of Player, and if you have several players, each new Player will have a higher number of points to start out with than the one before. That's probably not what you want. You don't really say what it is that you want, so let me throw out one thing that you could want and tell you how to code it. If that's not right, we'll go from there.
Edit:
OK, so now we know that you want to be able to just increment the earned points, so this Class now becomes much simpler (check the revision history of the post to see what parts were removed):
package{
public class Player extends EventDispatcher {
protected var _earnedPoints:int;
public function Player() {
super();//not passing a parameter, so target of events will be this
}
public function get earnedPoints():int {
return _earnedPoints;
}
public function set earnedPoints(value:int):void {
if (value != _earnedPoints) {
_earnedPoints = value;
//other Classes can listen for this to see if earnedPoints has changed:
dispatchEvent(new Event('earnedPointsChanged');
}
}
}
}
You didn't ask about this part, but I think it's a necessary part for you to understand. I'm going to explain it just in case it's not obvious. Your "objects" need to have a Class of their own:
package {
class TargetObject extends MovieClip {
public var points:int;
}
}
You can set those values when each TargetObject is created, or you can create several subclasses, where the value is hard-coded.
So now, whenever there's a collision, you can do something like this:
_player.earnedPoints += target.points;
When you set that variable, your PlayerClient will update, even when it is set from another Class.
In summary, you need to generate an event that other instances can listen for to update themselves:
package {
public class PlayerClient extends MovieClip {
public var playerScoreText:TextField;//assume this is on stage
protected var _player:Player;
//assume [dependency injection][1]
public function get player():Player {
return _player;
}
public function set player(value:Player):void {
if (value != _player) {
if (_player) {
//[prevent memory leaks][2]
_player.removeEventListener('earnedPointsChanged', updatePlayerScore);
//don't need this if we don't have a player
removeEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
_player = value;
if (_player) {
//listen for changes
_player.addEventListener('earnedPointsChanged', updatePlayerScore);
//we added a listener to the new player, need to make sure is removed
addEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
}
}
protected function cleanUp(e:Event):void {
_player.removeEventListener('earnedPointsChanged', updatePlayerScore);
}
/* This will trigger no matter what changes the value of
earnedPoints or bonusPoints.
*/
protected function updatePlayerScore(e:Event):void {
if (playerScoreText) /*in case it wasn't put on stage */ {
playerScoreText.text = _player.earnedPoints
}
}
}
}
I got my question resolved by myself: I used SharedObject method to store the points and then retrieve the point value back into my class Player for accumulation. EventDispatcher did pass the points to the other class, but all the points value get back to initial state once Player hits the obstacle and the game replays, which makes it difficult to retrieve the points I passed to the other class back to the class Player for accumulation. SharedObject method did help me resolve this problem for my case study.

Best way to manage EventListener on a large scale application in actionscript and Flex

I have a big mobile application in flex 4.5, and using "big" I mean big..
50+ views
50+ custom components
10+ custom events
50+ custom classes (not UIComponents)
Lot of skins, etc...
I never removed an EventListener, and now I'm looking at the flash profiler and I there are a lot of memory leaks..
I actually thought that using navigator.pushView or navigator.popView() or navigator.popToFirstView() will remove any reference of all the object/variable in the view itself and garbaged collected..
I'm trying to fix my code but I have a lot of problems understanding how to works with event listeners...
I think that it will be easier if I use some examples instead of describing every possible case...
Example 1:
private function XXX():void
{
var x:ClassA = new ClassA();
x.addEventListener(CustomEvent.GETA, doSomething);
x.addEventListener(CustomEvent2.TESTB, doSomethingElse);
}
private function doSomething(e:CustomEvent):void{}
private function doSomethingElse(e:CustomEvent2):void{}
Do I need to remove both eventListener after one is fired?
if yes, do I need to remove both event listener both in doSomething and in doSomethingElse?
Is it better to use a weakReference in the addeventlistener?
x.addEventListener(CustomEvent.GETA, doSomething, false, 0, true);
Example 2:
tile is a TileGroup, but it could be everything...
protected function activate(event:ViewNavigatorEvent):void
{
tile.removeAllElements();
for (var i:int = 1 ; i < functionCount.length ; i++)
{
var t:ImageButton = new ImageButton();
t.label = "";
switch (functionCount.getItemAt(i))
{
case "Val1":
t.addEventListener(MouseEvent.CLICK,function():void{goToAgenda();});
break;
case "Val2":
t.addEventListener(MouseEvent.CLICK,function():void{goToAdmin();});
break;
case "Val3":
t.addEventListener(MouseEvent.CLICK,function():void{goToMyStore();});
break;
case "Val4":
t.addEventListener(MouseEvent.CLICK,function():void{openBI();});
break;
}
tile.addElement(t);
}
The click on the ImageButton call a Function that does somethings and than call the navigator.pushView
If I change the view, should I remove every EventListener from every button?
Where is the best place to do that?
If I have a view with 15000 compoenents, do I have to manually remove all the eventlistener added to each one when the view is removed???
EDIT
I mainly need to remove all event listener when a view is removed from stage so instead of doing it manually every time I thought an alternative and I create a simple class...
package utils
{
import mx.collections.ArrayCollection;
public class Evt
{
public static var listaEvt:ArrayCollection = new ArrayCollection();
private var object:* = null;
private var event:* = null;
private var functio:* = null
public function Evt(obj:*, evt:*, funct:Function)
{
this.object = obj;
this.event = evt;
this.functio = funct;
}
public static function addEvt(obj:*, evt:*, funct:Function):void
{
var t:Evt = new Evt(obj, evt, funct);
listaEvt.addItem(t);
}
public static function clear():void
{
var tmpArr:ArrayCollection = new ArrayCollection();
tmpArr.addAll(listaEvt);
listaEvt = new ArrayCollection();
for (var i:int = 0 ; i < tmpArr.length ; i++)
{
var t:Evt = tmpArr.getItemAt(i) as Evt;
if (t.object != null && t.event != null && t.functio != null)
{
if (t.object.hasEventListener(t.event))
t.object.removeEventListener(t.event, t.functio);
}
}
}
}
}
In my code anytime I do not need weak reference I call (also using anonymous function):
// t can be any component with a event associated...
var f:Function = function():void{navigator.pushView(FotoGallery,data);};
Evt.addEvt(t, MouseEvent.CLICK, f);
t.addEventListener(MouseEvent.CLICK,f);
Then when I enter a new view I do a simple Evt.clear();
Is this approach effective? any advise to improve/change it?
Ultimately larger applications may benefit from frameworks such as Swiz Framework or Pure MVC, which can assist in:
Inversion of Control / Dependency Injection
Event handing and mediation
A simple life cycle for asynchronous remote methods
A framework that is decoupled from your application code
Swiz, for example, can use meta data tags to mediate events either catching events from the display list or injecting a dispatcher for events originating from non-UI sources.
[Dispatcher]
public var dispatcher:IEventDispatcher;
[EventHandler( event="UserEvent.ADD_USER" )]
public function handleAddUserEvent( event:UserEvent ):void
{
// do stuff
}
Otherwise, as mentioned, weak references in event listeners is an option:
addEventListener(Event.COMPLETE, completeHandler, false, 0, true);
Views requiring isolated event handling can adapt to the intrinsic lifecycle of added and removed from stage. Add listeners on addedToStage when the display object is ready to handle signals from the display list, and dispose listeners on removedFromStage when the display object no longer requires event handling.
package
{
import flash.display.Sprite;
import flash.events.Event;
public class MyClass extends Sprite
{
public function MyClass()
{
super();
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
protected function addedToStageHandler(event:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
/* add event listeners here */
}
protected function removedFromStageHandler(event:Event):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
/* remove event listeners here */
}
protected function dispose():void
{
/* any remaining cleanup */
}
}
}