Go Back   kirupaForum > Flash > ActionScript 3

Reply
 
Thread Tools Display Modes
Old 09-03-2006, 12:20 PM   #196
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Detecting Addition to or Removal from Stage

The added and removed events in ActionScript 3 let you detect when a display object is added to or removed from another display object container. These events, however, only work for the immediate parent of that display object. This means a sprite instance can be removed from the stage and not know it if its parent was removed from the stage and it continues to exist within the parent. Currently, there is no way to detect when this happens with Flash Player 9.0. The following StageDetection class (com.senocular.events.StageDetection), can be used to do so, however:
Code:
package com.senocular.events {
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	
	/**
	 * StageDetection 
	 * Lets a DisplayObject know when its been added to
	 * a display list attached to the stage.
	 *
	 * Usage:
	 * // used in the constructor where addedToStage and removedFromStage
	 * // are event handler methods
	 * var stageDetect:StageDetection = new StageDetection(this);
	 * stageDetect.addEventListener(StageDetection.ADDED_TO_STAGE, addedToStage);
	 * stageDetect.addEventListener(StageDetection.REMOVED_FROM_STAGE, removedFromStage);
	 */
	public class StageDetection extends EventDispatcher {
		
		protected var target:DisplayObject;
		protected var parents:Dictionary;
		protected var detect:String;
		
		public static const ADDED_TO_STAGE:String = "addedToStage";
		public static const REMOVED_FROM_STAGE:String = "removedFromStage";
		
		/**
		 * Constructor
		 */
		public function StageDetection(targetObject:DisplayObject) {
			target = targetObject;
			
			// determine whether need to detect for added or removed
			detect = (target.stage == null) ? Event.ADDED : Event.REMOVED; 
			
			// update parent listeners for detect events
			updateListeners();
		}
		
		/**
		 * updates listeners from which ADDED and REMOVED events are to be received
		 */
		protected function updateListeners(newDetect:String = null):void {
			
			// cleanup old listeners in parents
			if (parents) {
				for (var key:Object in parents) {
					key.removeEventListener(detect, stageCheck, false);
				}
			}
			
			// new event to detect if given
			if (newDetect) detect = newDetect;
				
			// set up detect event with current parents
			parents = new Dictionary(true);
			var parent:DisplayObject = target;
			while (parent) {
				parent.addEventListener(detect, stageCheck, false, 0, true);
				parents[parent] = true;
				parent = parent.parent;
			}
		}
		
		
		/**
		 * event handler for ADDED and REMOVED events checking for stage access
		 */
		protected function stageCheck(evt:Event):void {
			
			// only check for originating object
			if (evt.target != evt.currentTarget) return;
				
			// evt.type either ADDED or REMOVED
			switch(evt.type) {
				
				// parent added to another display object
				case Event.ADDED:
					
					// stage available, added to stage
					if (target.stage != null) {
						
						// added to stage, dispatch event, update
						dispatchEvent(new Event(ADDED_TO_STAGE));
						updateListeners(Event.REMOVED);
						
					// stage inaccessible, added to some other display object not on stage
					} else {
						
						// display list has changed, update parent listeners
						updateListeners();
					}
					break;
					
				// parent removed from stage
				case Event.REMOVED:
					
					// removed from stage, dispatch event, update
					dispatchEvent(new Event(REMOVED_FROM_STAGE));
					updateListeners(Event.ADDED);
					break;
			}
		}
	}
}
Note: As of Flash Player 9.0.28.0, Event.ADDED_TO_STAGE ("addedToStage"), and Event.REMOVED_FROM_STAGE ("removedFromStage") are built into the player.

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 01:02 PM   #197
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Event Phases and Event Capturing

Along with event propagation in ActionScript 3 comes different phases of events with display objects. With propagation, you have events being propagated from display objects to other display objects, such as mouse click events being propagated from children to their parents. This lets clicks within children objects to be recognized by parents (since children make up the contents of their parents, its only natural for the parent to also have those same events). The phases of such an event represent the progression of the event as it makes its way through the parent and child objects.

Events actually start with parent objects (phase 1: capturing), starting with the top most parent (stage) and making its way down to the child where the event originated (phase 2: at target). Then after reaching the child it makes its way back up through all the parents again (phase 3: bubbling).

Phase 1: Capturing
Code:
        event
+---------|----+
| parent  |    |
|  +------V-+  |
|  |child   |  |
|  |        |  |
|  +--------+  |
+--------------+
Phase 2: At Target
Code:
+--------------+
| parent       |
|  +------ -+  |
|  |child   |  |
|  |event X |  |
|  +--------+  |
+--------------+

Phase 3: Bubbling
Code:
+---------^----+
| parent  |    |
|  +------|-+  |
|  |child | |  |
|  | event| |  |
|  +--------+  |
+--------------+
The default listening behavior for addEventListener is to listen for both phases 2 and 3, the at target and bubbling phases. This lets your listener recieves events the target object recieves as well as all of those that bubble up from its children. Note: the bubbling phase does not include the event target, but since addEvent listener listens to both phases by default, it includes the target (at phase) with its default bubbling listening.

If you would want to listen for the capturing phase, addEventListener provides a third argument that allows you to do so.
Code:
var useCapture:Boolean = true;
target.addEventListener("event", listener, useCapture);
This, instead, allows a listener to receive events during the capture phase of an event. Unlike when not using capture (default behavior of addEventListener), using capture does not allow the listener function to be called when the event reaches the target at the at target phase. It only listens for the capturing phase.

To listen to all phases, you would use addEventListener twice
Code:
target.addEventListener("event", listener, true); // capturing
target.addEventListener("event", listener, false); // at target and bubbling

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 01:10 PM   #198
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Sorry about the delay from before. I'll crank out a few more to keep myself ahead (though time will probably catch up quickly)

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 01:20 PM   #199
AdamSchroeder
Registered User
Location Dayton, OH

Posts 63
These are great tips Senocular! Thank you so much for taking the time to write them and explain them.

I was wondering if you give a tip on the best way to capture the onMouseReleaseOutside event that existed in AS2.0 but no longer exist in AS3.0.

It would fit right in with your current tips explaining how the event system works.
AdamSchroeder is offline   Reply With Quote
Old 09-03-2006, 01:25 PM   #200
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Determining Event Phase

Event listener functions in ActionScript 3 can dermine what phase they're being called from using the Event object (flash.events.Event) that gets passed into function during the event. Each Event instance contains an eventPhase property that relates to the current phase of the event.

The eventPhase property is an integer value representing the phase. These values are contained as static properties of the EventPhase class (flash.events.EventPhase). They are as follows:
  • EventPhase.CAPTURING_PHASE
  • EventPhase.AT_TARGET
  • EventPhase.BUBBLING_PHASE

Example:
Code:
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

circle.addEventListener(MouseEvent.CLICK, clickCircle);
circle.addEventListener(MouseEvent.CLICK, clickCircle, true);
stage.addEventListener(MouseEvent.CLICK, clickStage);
stage.addEventListener(MouseEvent.CLICK, clickStage, true);

function clickCircle(evt:MouseEvent):void {
	trace("clickCircle: " + getPhaseName(evt.eventPhase));
}
function clickStage(evt:MouseEvent):void {
	trace("clickStage: " + getPhaseName(evt.eventPhase));
}

function getPhaseName(phase:int):String {
	switch(phase) {
		case EventPhase.CAPTURING_PHASE:
			return "Capturing Phase";
		case EventPhase.AT_TARGET:
			return "At Target Phase";
		case EventPhase.BUBBLING_PHASE:
			return "Bubbling Phase";
	}
	return "Error: No Phase Detected";
}
Clicking around on the circle you can see the different phases as they occur within the movie. Note that the using capturing with addEventListener on the circle does nothing in this example since the circle has no children that would allow an event to pass through the circle for a capture phase.

clicking on circle output
Code:
clickStage: Capturing Phase
clickCircle: At Target Phase
clickStage: Bubbling Phase
clicking on stage output
Code:
clickStage: At Target Phase

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 01:25 PM   #201
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Quote:
Originally Posted by AdamSchroeder
These are great tips Senocular! Thank you so much for taking the time to write them and explain them.

I was wondering if you give a tip on the best way to capture the onMouseReleaseOutside event that existed in AS2.0 but no longer exist in AS3.0.

It would fit right in with your current tips explaining how the event system works.
Yes, I'm working up to that one

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 01:56 PM   #202
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Preventing Event Propagation

If you want to prevent an event from propagating further, you can stop it from doing so within an event listener using stopPropagation() (flash.events.Event.stopPropagation()) or stopImmediatePropagation() (flash.events.Event.stopImmediatePropagation()). These methods are called from the Event objects passed into event listeners and essentially stop the event from happening - at least past that point.

stopPropagation prevents any objects beyond the current from recieving the event, and this can be within any phase of the event. stopImmediatePropagation does the same but also takes the extra step of preventing additional events within the current target receiving the event from happening too. So where as stopPropagation would prevent sprite A's parent from receiving the event, stopImmediatePropagation would prevent sprite A's parent as well as any other listeners listening to sprite A from receiving the event.

Example: toggle between using stopPropagation and stopImmediatePropagation
Code:
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

circle.addEventListener(MouseEvent.CLICK, clickCircle1);
circle.addEventListener(MouseEvent.CLICK, clickCircle2);
stage.addEventListener(MouseEvent.CLICK, clickStage);

function clickCircle1(evt:MouseEvent):void {
	evt.stopPropagation();
	// evt.stopImmediatePropagation();
	trace("clickCircle1");
}
function clickCircle2(evt:MouseEvent):void {
	trace("clickCircle2");
}
function clickStage(evt:MouseEvent):void {
	trace("clickStage");
}
Click the circle and see how the event is stopped with each method. stopPropagation prevented the stage from receiving the event while stopImmediatePropagation also prevented clickCircle2 from recognizing the event

normal output
Code:
clickCircle1
clickCircle2
clickStage
stopPropagation output
Code:
clickCircle1
clickCircle2
stopImmediatePropagation output
Code:
clickCircle1

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 02:13 PM   #203
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Global Events

Sometimes in ActionScript you'll want to determine when an event happens globally, whether it starts on this object or that object or even no objects. For mouse clicks, ActionScript 1 and 2 had onMouseDown and onMouseUp events which were called globally despite the location of the mouse and which object it was over. If the mouse was clicked, these events would fire. In ActionScript 3, these events now only relate to mouse events specific to the object under the mouse. A normal sprite would not receive the mouseUp event if the mouse was over an empty area of the stage when the mouse was released.

To get these events to be recognized globally within ActionScript 3, you will need to have to use event listeners with the Stage object. Since the stage is the end parent of all display objects, all events pass through the stage.

Now, one might think a simple use of addEventListener with a listener function would be all you need, but its not that easy. What happens when someone listens for an event like mouseDown for a sprite and then stops propagation for that event? Then the event would never bubble up to reach the stage. However, the event would still reach the stage in the capturing phase so if a listener is set for the stage using capture, it will be recognized before it even reaches the target. This, however, does not account for instances where the stage itself was clicked. For that, you would need to use addEventListener twice for the stage, one for captures and one to get the at target phase of the event if the stage is clicked. The only problem with this is that if an instance on the stage is clicked and it doesn't stop propagation, the stage would receive the event twice, once during capture and once during bubbling. To prevent this, you can just have your listener function only react to the capturing and at target phases by checking the eventPhase property of the Event instance passed to it.

Example:
Code:
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

// using stage for a global mouse up
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp, true);
function mouseUp(evt:MouseEvent):void {
	// only care about capturing and at target phases
	if (evt.eventPhase == EventPhase.BUBBLING_PHASE) return;
	
	trace("global mouseUp");
}

// circle stops propagation in its mouse up
circle.addEventListener(MouseEvent.MOUSE_UP, mouseUpCircle);
function mouseUpCircle(evt:MouseEvent):void {
	trace("mouseUpCircle");
	evt.stopPropagation();
}
Even though the circle here stops propagation of the event, the stage is still able to receive the event because it uses the capturing phase.

click stage output
Code:
global mouseUp
click circle output
Code:
global mouseUp
mouseUpCircle

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 02:40 PM   #204
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Detecting a mouseUp Outside

Unlike ActionScript 1 and 2, there is no onReleaseOutside event for ActionScript 3. mouseDown (Event.MOUSE_DOWN) and mouseUp (Event.MOUSE_UP) events only fire for display objects when the mouse is over the display object. The only way to detect a mouseUp outside is to detect a global mouseUp after the target instance recieves a mouseDown. If the target of that mouseUp event is then not the object originally within the mouseDown, you have a mouseUpOutside. To handle a global mouseUp, you would check for mouseUp events coming from the stage.

The easiest way to go about this is to define one mouseDown event handler for your desired object, then, in that handler, define the stage mouseUp handler. stage access shouldn't be a problem in the mouseDown since for the user to click on the target object, it would have to be in the display list, so stage access is not something to worry about. The mouseUp handler will fire when the mouse is released for the stage, and this means anywhere, even if the mouse is not within the Flash movie. Once that happens, you can check the event.target to see if the mouseUp is happening for your original object (be sure to also check instances within it) or something else such as the stage or another display object.

Example:
Code:
// create a circle to click
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

circle.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
function mouseDown(event:MouseEvent):void {
	// down, now check for global mouseUp
	stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
}
function mouseUp(event:MouseEvent):void {
	if (event.target == circle || circle.contains(event.target)){
		// mouse is up over circle (onRelease)
		// (if circle is not a DisplayObjectContainer, 
		// you do not need to use the contains check)
	}else{
		// mouse is up outside circle (onReleaseOutside)
	}
	
	// be sure to clean up stage listener
	stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
}

__________________
senocular is offline   Reply With Quote
Old 09-03-2006, 02:41 PM   #205
saxplayer13
Error: File Not Found
 
saxplayer13's Avatar
Location In front of my iMac G5

Posts 55
@Senocular:

How would you direct all keyboard events to one class? I've tried doing this in a game I'm developing, but it is very buggy and it keeps losing focus... I think what I need is a way to lock focus on one DisplayObject, but I can't seem to figure it out.

__________________
-bash: yourmom: command not found
IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!
*** Internet Explorer users, click here. ***
saxplayer13 is offline   Reply With Quote
Old 09-03-2006, 02:46 PM   #206
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Quote:
Originally Posted by saxplayer13
@Senocular:

How would you direct all keyboard events to one class? I've tried doing this in a game I'm developing, but it is very buggy and it keeps losing focus... I think what I need is a way to lock focus on one DisplayObject, but I can't seem to figure it out.
Use the stage. See the tips above ^^^

__________________
senocular is offline   Reply With Quote
Old 09-04-2006, 03:31 PM   #207
AdamSchroeder
Registered User
Location Dayton, OH

Posts 63
Thanks for the these latest tips. Particurarly the onMouseReleaseOutside example was very helpful. All of these tips are so useful... saves you so much time seeing them explained properly with examples vs. trial and error on your own to figure out.

I do want to add one tip that others new to AS3.0 might run into. The stage is now longer global but is a property of a DisplayObject IF it has been attached to a DisplayObject already placed on the stage.

So
var s:Sprite=new Sprite();
s.stage //is null
addChild(s)
s.stage //correctly reference the stage.

In pratical terms if you are extending the MovieClip to make your own classes you can not add events to the stage in your constructor (unless an argument you pass has access to it).

I had to pull my event adding code into a seperate function to call after the movieclip was already added.

example:
var e:ExtendedMovieClipClass = new ExtendedMovieClipClass();
addChild(e);
e.setupEvents();

Maybe you could get a reference from the actual Stage (capital S) class, but I haven't looked into that yet.
AdamSchroeder is offline   Reply With Quote
Old 09-04-2006, 04:09 PM   #208
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
That is correct. The stage property is only accessible from display objects attached to the stage. There has been some discussion about this and approaches to access the stage eariler in this thread.

__________________
senocular is offline   Reply With Quote
Old 09-06-2006, 11:05 AM   #209
senocular
On Vacation
 
senocular's Avatar
Location San Francisco, CA (USA)

Posts 17,426
Flash 9: Document Class

ActionScript 3 with Flash 9 lets you specify a "Document Class" (aka Application class) for the main timeline. This represents the class of the root object - the display object in which (essentially) all other display objects are placed.

You can set the Document class in the Document Class option of the Property Inspector for a document with nothing selected. You can also set the Document Class in the ActionScript 3 Settings dialog using File > Publish Settings > Flash [Tab] > Settings... [Button (for ActionScript 3)]. When defining the Document class, you simply provide the name of the class which is to pose as the class for your document. Note: This class should exist within your class path.

The Document Class for Flash needs to extend Sprite or any subclass of Sprite (flash.display.Sprite) to be valid. If you use the main timeline for ActionScript at all in Flash, you would want your document class to extend MovieClip (flash.display.MovieClip) since MovieClips, unlike Sprites, support frames.

A document class should also be public (along with its constructor which is inherently public). A non-public class will result in an error when you publish your movie.

When published from Flash, SWF meta tags within Document Classes are ignored and the settings within Flash are used.

Unlike other display object classes you make, the stage property of a Document class will always be accessible since the document class is inherently a child of the stage. Similarly, if you place any objects on the main timeline in Flash, the will already be children of the Document class when its instantiated.

Example of a Document class:
Code:
package {

	import flash.events.Event;
	import flash.display.MovieClip;
	
	public class CustomDocument extends MovieClip {
		
		public function CustomDocument() {
			addEventListener(Event.ADDED, checkChildren);
			checkChildren(new Event("initialize"));
		}
		
		private function checkChildren(evt:Event):void {
			
			// only allow one child in root
			if (numChildren > 1) {
				throw (new Error("This movie can have only one child instance"));
			}
		}
	}
}
This class only allows one child. If more are added, an error is thrown. Notice that the checkChildren method is also automatically called in the constructor since its possible that objects could have been added to the timeline within Flash which would have been before the added event listener was created.

__________________
senocular is offline   Reply With Quote
Old 09-07-2006, 02:05 AM   #210
v_gyku
Registered User
Will the following code run? or is it somthing more I need to code?
I copy pasted the following code in the actions panel and tested the movie.
It gave a compile error: 1037: Packages cannot be nested.
Output window: ReferenceError: Error #1065: Variable MainTimeline is not defined.

Am i doing somthing wrong?

Quote:
Originally Posted by mathew.er
So that example at livedocs makes it pretty clear... "stage" is a global object allowing you to view and change its properties or to register events with it.

ActionScript Code:


package {</p>


<p> import flash.display.Sprite;</p>
<p> import flash.display.StageAlign;</p>
<p> import flash.display.StageScaleMode;</p>
<p> import flash.events.Event;</p>
<p>&nbsp;</p>
<p> public class StageExample extends Sprite {</p>
<p>&nbsp;</p>
<p> public function StageExample() {</p>
<p> stage.scaleMode = StageScaleMode.NO_SCALE;</p>
<p> stage.align = StageAlign.TOP_LEFT;</p>
<p> stage.addEventListener(Event.ACTIVATE, activateHandler);</p>
<p> stage.addEventListener(Event.RESIZE, resizeHandler);</p>
<p> }</p>
<p>&nbsp;</p>
<p> private function activateHandler(event:Event):void {</p>
<p> trace("activateHandler: " + event);</p>
<p> }</p>
<p>&nbsp;</p>
<p> private function resizeHandler(event:Event):void {</p>
<p> trace("resizeHandler: " + event);</p>
<p> trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight);</p>
<p> }</p>
<p> }</p>
<p>}




@fabiopb: Why? Isnt this just a sign, that flash goes on pretty well? If your worried anout the Adobe Flash application itself, then you dont need to be... Flex is just for something else than Flash. It wont also disappear from the Internet as everybody has the plugin, people are used to use it and it still has great potential.

Last edited by v_gyku; 09-07-2006 at 03:36 AM..
v_gyku is offline   Reply With Quote
Reply


Currently Active Users Viewing This Thread: 6 (0 members and 6 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 10:55 PM.

SHARE:

SUPPORTERS:

cdn
content delivery network (cdn)

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd. Copyright 2010 - kirupa.com Copyright 2010 - kirupa.com