A Flash Developer Resource Site

Results 1 to 7 of 7

Thread: [RESOLVED] Event.ADDED_TO_STAGE dispatched twice?

Hybrid View

  1. #1
    Lunatic
    Join Date
    Nov 2002
    Location
    AS3 Forum
    Posts
    342

    resolved [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.

  2. #2
    Total Universe Mod jAQUAN's Avatar
    Join Date
    Jul 2000
    Location
    Honolulu
    Posts
    2,429
    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{

        }
      }
    }

  3. #3
    Lunatic
    Join Date
    Nov 2002
    Location
    AS3 Forum
    Posts
    342
    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.

  4. #4
    Total Universe Mod jAQUAN's Avatar
    Join Date
    Jul 2000
    Location
    Honolulu
    Posts
    2,429
    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.

  5. #5
    Lunatic
    Join Date
    Nov 2002
    Location
    AS3 Forum
    Posts
    342
    No that makes perfect sense. Thank you very much for taking the time to explain all this.

  6. #6
    Lunatic
    Join Date
    Nov 2002
    Location
    AS3 Forum
    Posts
    342
    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?

  7. #7
    Total Universe Mod jAQUAN's Avatar
    Join Date
    Jul 2000
    Location
    Honolulu
    Posts
    2,429
    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
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center