Hai,
I want to override the dispatchEvent method while inheriting from flash.display.Sprite. Whenever an event gets dispatched now like ADDED_TO_STAGE and CLICK it will get through dispatchEvent in my theory. However there is no single trace he'll get through dispatchEvent.. so the events get dispatched internally from some where else?
public class TestSprite extends Sprite
{
public function TestSprite()
{
this.addEventListener(Event.ADDED_TO_STAGE, this.handleAddedToStage);
}
private function handleAddedToStage(event:Event):void
{
}
override public function dispatchEvent(event:Event):Boolean
{
trace(event.type);
return super.dispatchEvent(event);
}
}
this._sprite = new TestSprite();
this._sprite.graphics.beginFill(0x000000);
this._sprite.graphics.drawRect(20, 20, 200, 200);
this._sprite.graphics.endFill();
this.addChild( this._sprite );
There is no trace..
The Sprite implements IEventDispatcher so that the coder - you - can dispatch custom events from display-list classes. As you suspected however, native Flash Player events are not dispatched through the dispatchEvent method itself, they are created and passed to event listeners internally. I imagine a primary reason for this is performance.
sooner or later in the function you need to pass the event to the real sprite eventDispatcher or it won't work (hint: super.dispatchEvent(event) )
by the way this will work only on objects that subclasses your own class, the others will continue to subclass Sprite, hence use the Sprite method.
Related
I'm working on a mobile AIR project using Flash Builder 4.6. I'm dispatching a custom Event but it is never being heard. I've checked similar questions but cannot find an answer which solves it for me. In my main mxml file I add the listener in the addedToStage event:
[EDIT] adding the listener to the userStatus instance. Still not working.
<s:ViewNavigatorApplication
...
private var userStatus:UserStatus;
protected function addedToStageHandler(event:Event):void
{
userStatus = new UserStatus();
userStatus.addEventListener(CustomEvent.CREDENTIALS_READY, credentialsReadyHandler);
The CustomEvent class:
public class CustomEvent extends Event
{
public static const CREDENTIALS_READY:String = "credentialsReady";
public function CustomEvent(customEventString:String){
super(customEventString, true, false);
}
}
The class which dispatches the CustomEvent after a successful service call:
public class UserStatus extends EventDispatcher
{
...
//event handler
private function userStatusLoaded(e:Event):void
{
var json:Object = JSON.parse(paidUserLoader.data.toString());
if(json.success == "true")
{
trace("UserStatus::this is a paid user!");
dispatchEvent(new CustomEvent(CustomEvent.CREDENTIALS_READY));
}
I can see the trace statement in the console, so I know the code is getting there. But the listener in the s:ViewNavigatorApplication class never fires.
Can anyone see why?
Thanks.
Any custom class event has to override the clone() method. This method is automatically called internally but if it's not present (overrided) then only direct dispatch can work but events cannot traverse a hierarchy.
EDIT: nvm, there's no reason for your ViewNavigatorApplication to actually catch that event since UserStatus is not a DisplayObject. You are confused about how event are dispatched and are supposed to work. Only the UserStatus itself can catch the event it is dispatching. No other object can. In the case of a display list event propagate but for all non displayobject they need to catch their own dispatch.
EDIT: Answering comment:
There's somewhere a reference of the UserStatus instance, it might be in the ViewNavigatorApplication or not, if it is then:
myuserstatus.addEventListener(CustomEvent.CREDENTIALS_READY, credentialsReadyHandler);
would work. If it's not there then you need to pass along the information from whether class having the reference to UserStatus to the ViewNavigatorApplication. This can be done by redispatching the event (this is where you need to override clone()).
The instance of someotherclass has a reference to userstatus and does listen for the event. When the event fires the someotherclass instance catches and redispatches it. The ViewNavigatorApplication listen for that event directly or via the someotherclass instance reference and finally catches the event.
EDIT: responding to new edits
Let's fix your custom event class. By default you set bubbling to true but only DisplayObject can bubble so it should be set to capture (false)
super(customEventString, false, false);
That should fix it I bet.
1) First of all I don't wanna use CustomEvent class. Some solution I am looking without using CustomEvent.
2) One of the solution can be having abc variable in ClassA. And then dispatching directly via ClassA ( rathar than saying classB.dispatchEvent() ). But still looking if there is some better solution than this.
//Frame1 code :
import flash.events.Event;
var classA:ClassA = new ClassA() ;
classA.addEventListener("hello", hello);
classA.init();
function hello(e:Event)
{
trace(e.currentTarget.abc); //<<<< NEVER EXECUTED
}
//classA
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class ClassA extends MovieClip
{
var classB:ClassB ;
public function ClassA()
{
classB = new ClassB();
}
public function init()
{
classB.dispatchEvent( new Event("hello"));
}
}
}
//classB
package
{
import flash.display.MovieClip;
public class ClassB extends MovieClip
{
public var abc:Number =123;
public function ClassB()
{
}
}
}
You are missing a couple key concepts before you can get your example to work. First you are dispatching the event on an instance of ClassB, however you are listening on an instance of ClassA. So, they have to be related in some way, in order for event to be properly orchestrated when it gets dispatched. One way to do that is to use event bubbling. One caveat to that is that native event bubbling only really works for DisplayObjects, but both of your classes inherit from MovieClip so thats not a big deal.
So the first thing, you have to understand how bubbling events work. A simplified explanation is that events start at the top of the display hierarchy and capture down the display tree towards the element, they are finally dispatched on the target, then they turn around and bubble back out in the opposite direction.
This means that your instance of ClassB has to be a child of ClassA. So the first thing you'll have to change is in your ClassA constructor:
public function ClassA()
{
classB = new ClassB();
addChild(classB);
}
Next, when you dispatch the event, you'll need to explictly say that its a bubbling event, otherwise it'll be triggered on the target, and neither capture nor bubble through the display stack.
public function init()
{
classB.dispatchEvent( new Event("hello", true));
}
The second argument of true sets the event to a bubbling event.
Finally you'll need to change your handler. Right now, it's using e.currentTarget, which isn't going to be what you expect in this case (usually it is, thought).
You have to understand the difference between e.target and e.currentTarget. e.target is the actual target of the event, independent of how its bubbling or capturing. e.currentTarget on the other hand is the element which is presently handling the event. So in your case e.currentTarget is an instance of ClassA (the instance that the event handler is actually attached to) and e.target is an instance of ClassB (the instance that the event was dispatched on). During the event lifecycle, e.currentTarget will change as the event moves around, but e.target should always be the same.
In this case, you want to reference the actual target of the event, not the element that is currently processing the event. So you need to change your handler to:
function hello(e:Event)
{
trace(e.target.abc);
}
And then it should work. You can find a working example here that encapsulates the changes I've described.
If these classes weren't DisplayObjects then you would have to take a different approach -- either by using a signal pattern or to manually listen for an retrigger the event inside ClassA.
First of all you are adding an event listener to classA but your classA init method is asking classB to dispatch an event and this is the reason why your code does not get executed. If you want to catch the hello event you should be doing something like
public function init()
{
this.dispatchEvent( new Event("hello"));
}
Or you should be registering the listener on classB (which is not in scope so no code suggestion).
In ActionScript the best approach to transfer information is to use custom events so my suggestion is to re evaluate your decision on custom events.
i am building a game on flash cs5. i am making the start screen, and now i try to load the game but I get an error 1180 at my play game function. here is how it works
this is the function were i get the error at line this.stageRef. This class is my mainMenu which extends basemenu.
private function playGame(e:MouseEvent) : void
{
unload();
this.stageRef.dispatchEvent(new Event("gameSTART"));
}
and here is my engine function
public function Engine()
{
preloader = new ThePreloader(474, this.loaderInfo);
stage.addChild(preloader);
preloader.addEventListener("loadComplete", loadAssets);
preloader.addEventListener("preloaderFinished", showMenu);
stage.addEventListener("gameSTART", fGameStart);
}
private function fGameStart(e:Event):void
{
.......... here is all my game code
}
It seems, your stageRef is not proper EventDispatcher object. Either you have another custom Stage class, or when you get stage property with Stage object the owner of this property is not on the stage yet. So try to get stage property after Event.ADDED_TO_STAGE event of the source object. Or show your code where you get Stage and pass to the MainMenu.
make stageRef class implement IEventDispatcher
public class Framework extends MovieClip
{
var _loadingSystem:LoadingSystem;
public function Framework()
{
_loadingSystem = new LoadingSystem(this);
loaderInfo.addEventListener(ProgressEvent.PROGRESS,progressHandler);
loaderInfo.addEventListener(Event.COMPLETE, completeListener);
}
...
public class LoadingSystem extends MovieClip
{
public function LoadingSystem(parent:DisplayObjectContainer)
{
parent.addChild(this);
myLogo.buttonMode = true;
myLogo.addEventListener(MouseEvent.CLICK, gotoMySite);
}
As you can see, Framework is my Doc class which is creating _loadingSystem which is basically a movieclip that contains the preloader graphics. When I debug I get the following error "TypeError: Error #1009: Cannot access a property or method of a null object reference." pointing to myLogo.buttonMode = true;
From what I understand this is due to LoadingSystem not being fully loaded before being created in Framework. Is there any way for me to make this work? I have tried adding listeners for Event.ADDED but it didn't work.
Additional info: 3-frame FLA, first empty with a stop, second holding an AssetHolder movieclip, third for the application. I have export on 2nd frame set in publishing settings, all checkboxes for export on 2nd frame unchecked in the assets, and this all worked before I changed the export on 2nd frame setting except it wasn't preloading 50% of the file.
i think what's happening is this:
A document class is ALWAYS loaded in the first frame, because it represents your swf root class and thus has to be there in the first frame. Now, since you export all the other classes to frame 2, i would imagine, that LoadingSystem is existing only beginning with frame two, but you try to instantiate it in the constructor of your document class Framework.
What you could try out is, create a method "initialize" in Framework and call that from the timeline in frame 2. And in that method you would do the stuff, you currently do in the constructor of Framework.
if myLogo is a sprite/movieclip on the stage, it wont exist until LoadingSystem is added to the stage.
Now your first reaction should be "but I added it to the stage with parent.addChild(this)!". What you didn't take into account is that the document class isn't on the stage when the constructor is called. Flash basically executes like this:
docClass = new DocumentClass();
stage.addChild(docClass);
Which means that the stage property of the document class will be null until after the constructor is finished. It also means that any children added during the constructor wont have access to the stage or objects located on the stage until after the docClass is added to the stage.
There is a simple fix; listen for the ADDED_TO_STAGE event.
public function LoadingSystem(parent:DisplayObjectContainer)
{
parent.addChild(this);
addEventListener(Event.ADDED_TO_STAGE, initialize);
}
private function initialize(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initialize);
addEventListener(Event.REMOVED_FROM_STAGE, uninitialize);
//attach stage listeners etc
myLogo.buttonMode = true;
myLogo.addEventListener(MouseEvent.CLICK, gotoMySite);
}
private function uninitialize(e:Event):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, uninitialize);
addEventListener(Event.ADDED_TO_STAGE, initialize);
//detach stage listeners etc.
}
what would be the simpliest way to communicate between independent classes?
I have been searching for two days and couldn't find nothing,
isn't there a way that one class has a dispatchEvent and the other an addEventListener, with a custom Event?? I could only find solutions within the same class o with a parentchild relationship, but what I'm looking for is like a "brother" relationship so to speak
thanks
In general, you want the class that dispatches the event to either extend EventDispatcher or implement IEventDispatcher. (All DisplayObjects do, so if your classes are DisplayObjects, you don't need to do anything extra.)
In the dispatching class:
class DispatchingClass extends Event Dispatcher {
function doSomething() {
// do stuff
dispatchEvent(new Event("FOO"));
}
}
In the listening class:
class ListeningClass {
function startListening(dispatcher:DispatchingClass) {
dispatcher.addEventListener("FOO", handleFoo);
}
function handleFoo(evt:Event) {
// do stuff
}
}
EventDispatchers work fine with custom events.
If for some reason your listening class doesn't have and can't get an instance of your dispatching class, you can make a global event broadcaster. Basically, make a universally-accessible class that extends EventDispatcher (or implements IEventDispatcher) and listens for and dispatches events to anything that tells it to.
Here is a bare-bones implementation of an event broadcaster:
import flash.events.EventDispatcher;
public class EventBroadcaster extends EventDispatcher {
private static var _instance:EventBroadcaster = new EventBroadcaster();
public function EventBroadcaster() {
if (_instance != null) {
trace ("Error: an instance of EventBroadcaster() already exists.");
}
}
public static function getInstance():EventBroadcaster {
return EventBroadcaster._instance;
}
}
You use it pretty much the same way:
class DispatchingClass {
function doSomething() {
// do something
EventBroadcaster.getInstance().dispatchEvent(new Event("FOO"));
}
}
class ListeningClass {
function startListening() {
EventBroadcaster.getInstance().addEventListener("FOO", handleFoo);
}
function handleFoo(evt:Event) {
// do stuff
}
}
dispatchEvent() and addEventListener() are just the functions from the built-in EventDispatcher.
There's some discussion at Event Broadcaster - Simple events solution... on ways to make an event broadcaster and how to add useful features. The article Centralized Event Management in Actionscript 2.0 has a good introduction to the concept.
So if you have ChildClass1, ChildClass2, and ParentClass, where ChildClass1 and ChildClass2 are both the children of ParenClass.
ChildClass1 will dispatch an event.
ParentClass will listen for this event, and then it's handler will update ChildClass2.
If you don't have a ParentClass, you could also use a ChildManagerClass that registers Childs and notifies them accordingly.