-
[RESOLVED] Event.ADDED_TO_STAGE dispatched twice?
I'm getting this weird glitch where the added_to_stage event seems to be dispatched twice when I construct my object.
Now I know that my constructor is not being called twice cause I traced a string before the event listener, and it only traces once but I traced the same string within the listener handling function body and it traced twice.
Here's the code:
Code:
package com.berubegraphics.martinemartell
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.Shape;
import flash.display.Graphics;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Rectangle;
import flash.events.*;
import flash.text.*;
import fl.transitions.Tween;
import fl.transitions.easing.*;
import fl.transitions.TweenEvent;
class ContentBox extends MovieClip
{
public var container:Shape;
public var titleBar:Shape;
public var boxTitle:TextField;
public var bW:int;
public var bH:int;
private var paramT:String;
private var paramW:Number;
private var paramH:Number;
private var paramWP:Boolean;
private var paramHP:Boolean;
private var tf:TextFormat;
public function ContentBox(t:String, w:Number, h:Number, wP:Boolean, hP:Boolean):void
{
paramT = t;
paramW = w;
paramH = h;
paramWP = wP;
paramHP = hP;
// traced "test" here and outputed test only once
var buildBox:Function = function(e:Event):void
{
// traced "test" here and outputed twice
if (wP == true)
{
bW = ((stage.stageWidth - 20) * w) / 100;
} else {
bW = w;
}
if (hP == true)
{
bH = ((stage.stageHeight - 180) * h) / 100;
} else {
bH = h;
}
container = new Shape();
container.graphics.beginFill(0x000000);
container.graphics.drawRect(0, 0, bW, bH);
container.graphics.endFill();
container.alpha = .5;
addChild(container);
titleBar = new Shape();
titleBar.graphics.beginFill(0x000000);
titleBar.graphics.drawRect(10, 10, bW - 20, 30);
titleBar.graphics.endFill();
addChild(titleBar);
tf = new TextFormat();
tf.align = TextFormatAlign.CENTER;
tf.size = 15;
tf.font = "Falconis";
tf.color = 0xFFFFFF;
boxTitle = new TextField();
boxTitle.antiAliasType = "advanced";
boxTitle.text = t;
boxTitle.sharpness = 0;
boxTitle.x = 14;
boxTitle.y = 14;
boxTitle.embedFonts = true;
boxTitle.multiline = false;
boxTitle.selectable = false;
boxTitle.setTextFormat(tf);
boxTitle.width = boxTitle.textWidth + 5;
boxTitle.height = 22.35;
addChild(boxTitle);
}
addEventListener(Event.ADDED_TO_STAGE, buildBox);
}
public function resizeBox():void
{
if (paramWP == true)
{
bW = ((stage.stageWidth - 20) * paramW) / 100;
} else {
bW = paramW;
}
if (paramHP == true)
{
bH = ((stage.stageHeight - 180) * paramH) / 100;
} else {
bH = paramH;
}
container.graphics.clear();
titleBar.graphics.clear();
container.graphics.beginFill(0x000000);
container.graphics.drawRect(0, 0, bW, bH);
container.graphics.endFill();
container.alpha = .5;
titleBar.graphics.beginFill(0x000000);
titleBar.graphics.drawRect(10, 10, bW - 20, 30);
titleBar.graphics.endFill();
}
}
}
Last edited by Beathoven; 08-19-2010 at 06:22 PM.
-
Total Universe Mod
It's due to event bubbling. You are getting one call for the ContentBox instance hitting the stage and another for one of the addChild() calls inside of buildBox(). You can't really pin down which one since you never really know which tick the flashplayer is on. That is to say, ContentBox may add some children to its display list before itself is actually on the stage.
You really shouldn't write local functions. buildBox() should exists at the class level the way resizeBox() is.
The proper use of added to stage is to assign as the only line of a constructor and pass it an init function as the handler. The init function should immediately remove the added to stage listener and procede to initialize the instance.
Actionscript Code:
package{ public class MyClass{ public function MyClass(){ if(!stage) { addEventListener(Event.ADDED_TO_STAGE, init); }else{ init(null); } }
private function init(e:Event):void{ removeEventListener(Event.ADDED_TO_STAGE, init); buildBox(); }
private function buildBox():void{
} } }
-
Thank you very much for your input sir.
Would you be kind enough to explain me the logic behind all this?
That init function seems like a bit of a waste with it's 2 lines of code.
-
Total Universe Mod
With the move to AS3, adobe eliminated a global Stage object. Instead, every DisplayObject has a .stage property. The downside is that the .stage property will come up null if the DO or its parents don't actually exist on stage.
This leads you to believe that as soon as you call addChild(), the child can reference the stage, for instance:
public var mySprite:Sprite = new Sprite();
trace(mySprite.stage); // null
addChild(mySprite);
trace(mySprite.stage); // maybe, maybe not
The reason why its a maybe is that there is a code/draw order that the flashplayer follows. If you try to reference the stage during the code (constructor, inits etc) before the draw happens, stage will be null still. In some cases flashplayer just happens to be drawing and the stage reference will work. But this is not reliable or predictable. Thus, the ADDED_TO_STAGE event. If your constructor/init doesn't need to reference the stage, its really not needed. But like in the case of your buildBox() function, you need to ensure .stage will be available. So basically you are saying, don't do a damn thing until you're actually present.
Now, the reason you were getting double calls is due to event bubbling. It's a rather important concept so reading up on it is recommended. Basically when something happens inside a DisplayObject, all of its parents hear about it. There's kind of a counter-intuitive boomerang path an event object takes consisting of 3 phases: Capture, Target and Bubbling. Wth does this mean?
Imagine this scenario, you have a Sprite on the main stage called theGrandParent. theGrandParent has one child Sprite called theParent who itself has a child called theChild. So you have the following hierarchy:
Code:
theGrandParent
|_theParent
|_theChild
Now lets say you are listening for mouse clicks on theChild, you'd have
theChild.addEventListener(MouseEvent.CLICK, myClickHandler);
What actually happens is odd, yet sensible. The first entity aware of the click is theGrandParent, then theParent. This is called the Capture phase. Then theChild is aware. This is known as the Target phase. Then, it goes in reverse back up the chain in the Bubbling phase.
This is useful in case you want theGrandParent to know about any click that happens to any of its children. Since you had ContentBox instances listening for ADDED_TO_STAGE, it was hearing about any ADDED_TO_STAGE events happening within it, in your case the addChild(container); addChild(titleBar); addChild(boxTitle); were all broadcasting their own. The reason you didn't get 4 traces is again because some of them got added to ContentBox before ContentBox actually got added so all though they were added to ContentBoxes display list, ContentBox hadn't actually hit the stage yet so neither did some children.
I hope that didn't confuse you too much.
Last edited by jAQUAN; 08-20-2010 at 10:53 AM.
-
No that makes perfect sense. Thank you very much for taking the time to explain all this.
-
Now just to make sure I'm still doing this the right way, I can still assign the values of the paramaters to some variables in the constructor function right?
-
Total Universe Mod
Sure, anything that doesn't require the .stage reference. Use your best judgement on things that may need to be set after the .stage exists.
I like to use init functions in some cases where I may need to reset the instance without creating a new one.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|