View Full Version : Events (Why oh why is this so hard?)
Jawnee
08-20-2008, 11:20 AM
Ok guys, I'm at my wits end here... and it's made worse by the fact that I KNOW this should be easy. I understand the concept simple enough but for whatever reason I cannot seem to implement communication using events and event listeners between my two swfs to save my life. Maybe it's because one is inside a UILoader... honestly at this point I have no idea.
Basically I have SWF1 with a navigation strip on it and SWF2 with a control for displaying streamed FLV movies on it. SWF2 is displayed within SWF1 using a UILoader. There are also SWF2, SWF3... etc which are essentially other content pages of the site. So if a user loads SWF2 (the movie page) and starts playing a movie, but then navigates away I need to stop playing the movie on the page. Immediately I thought, awesome, events are an obvious answer and they seem simple enough, but alas I keep failing and only seem to be confusing myself the more I read online.
I tried creating a custom event class shown here:
package {
import flash.events.Event;
public class NavEvent extends Event {
public static const NAVCHANGE:String = "navchange";
public function NavEvent(type:String) {
super(type,true);
}
public override function clone():Event {
return new NavEvent(type);
}
}
}
And then utilizing it within the swfs. So in SWF1, the navigation SWF, when a user clicks one of the navigation settings I do the following:
var dispatcher:EventDispatcher = new EventDispatcher();
dispatcher.dispatchEvent(new NavEvent(NavEvent.NAVCHANGE));
To me that makes sense, I'm making a navigation change so send out a notice to any pages that are listening so they can take appropriate action like stopping a movie in the case of the movie page.
So then on SW2, the movie SWF I do the following:
addEventListener(NavEvent.NAVCHANGE, onNavigateAway);
function onNavigateAway(event:Event):void {
trace("I heard a navigate away event");
ns.close();
}
Note: ns is my netstream object that streams the flv into my player
Then I thought... maybe I'm going about this wrong, maybe I don't need a custom event, just an event dispatcher of my own design that comes with those custom events already contained. So I created this class:
package {
import flash.events.EventDispatcher;
import flash.events.Event;
public class CustomDispatcher extends EventDispatcher {
public static var NAVCHANGE:String = "navchange";
public static var SHOWMOVIE:String = "showmovie";
public function doNavChange():void {
dispatchEvent(new Event(CustomDispatcher.NAVCHANGE));
}
public function doShowMovie():void {
dispatchEvent(new Event(CustomDispatcher.SHOWMOVIE));
}
}
}
And did the following
Calling SWF:
var cusDispatcher:CustomDispatcher = new CustomDispatcher();
cusDispatcher.doNavChange();
This didn't work either. I feel like I'm missing something fundamental and for whatever reason nothing I'm reading online is helping me make sense of this. I've used some events/eventlisteners in the past, mainly for external classes that listen for their own tasks to complete but his is the first time I've tried to have an event fire from one SWF and a second SWF do something as a result. At this point I would gladly welcome ANY assistance at all. As I said above, I have no idea why this wouldn't work. It seems like such a simple concept. Thanks for reading!
wvxvw
08-20-2008, 12:08 PM
Your dispatcher dispatches a bubbling event, bubbling events go up along the child-parent chain (from child to parent), are you sure the listening class is on that chain above the dispatcher? (To me it seems like you have exactly the opposite situation). So, I'd rather addEventListener every time the new SWF loads, and remove when it unloads, othervise you may get lost trying to understand where those events are coming from...
Mazoonist
08-20-2008, 02:26 PM
You should go to www.gotoandlearn.com, and watch the two-part tutorial on Object Oriented Scrollbar. You will learn how to create and use a custom event class. It's really good, and if you have the time, it's well worth watching. I've been through it several times.
One thing that's hard to get at first, is that the object that you add a listener to should be an instance of the same class that does the dispatching.
Hope that helps somewhat. The tutorial surely will.
Jawnee
08-21-2008, 12:13 AM
You should go to
One thing that's hard to get at first, is that the object that you add a listener to should be an instance of the same class that does the dispatching.
I was starting to suspect this late last night right after I posted this thread but at that point I was too tired to bother trying so I decided to call it a night. I'm loading up the tutorials you suggested now. Thanks for taking the time to respond to my plea for help. I'll come back and update if I have more detailed questions after watching the tutorial.
Cheers!
Jawnee
08-21-2008, 02:31 AM
Ok so after watching that video (thanks again for linking it) I guess I have two questions that could help point me on my way: As far as events go is it safe to say that the object that listens and the object that dispatches the message always have to be instances of the same class? Or for that matter, does the listener and the dispatcher have to be the same instance of the same class? That seems to be what this video is saying to me, but I'm not sure if that's a golden rule.
In the movie example his scrollbar class fires an event when he moves the thumb inside the scrollbar movie clip. But the only listener is that same scrollbar instance itself, inside the scrollbox. Is it impossible to have another object on the stage hear that same event?
Mazoonist
08-21-2008, 03:21 AM
Hey Jawnee,
I'm not sure it's a hard and fast rule either. I'm still learning myself. Lordofduct is really good with this stuff, perhaps he will provide you some further insight. For now, however, here's what it says on page 223 of "Essential Actionscript 3.0":
"The object on which dispatchEvent() is invoked is the event target." Which seems to be the same thing that Lee Brimelow's video tutorials demonstrate as well.
The process of creating a custom event would seem to be:
1. Make sure that your class dispatching the event somehow extends the EventDispatcher class. However, MovieClip and Sprite already inherit from EventDispatcher, so if your object is one of those, you're already good to go.
2. Somewhere in the code of the class, invoke the dispatchEvent() method. Inside the parentheses, create an instance of your custom event. Your custom event may or may not be an instance of a custom class that extends the Event class:
dispatchEvent(new MyCustomEvent(type));
(In the above case, you'd want to have written a class called MyCustomEvent that extends Event, and can be found in the classpath somehow. Or, just do this, using the generic Event class:
dispatchEvent(new Event(type));
Where type is just a string. The convention is to use public static variables or constants for the string. However, a literal string will also work. As long as whatever string you use is the same one specified in the addEventListener argument list.
3. In the code that instantiates your object, somewhere you will add an event listener to that object.
To illustrate that the process at its heart is really pretty simple, the following code on frame 1 of a new fla file works fine:
addEventListener("foo", fooListener);
function fooListener(event:Event) {
trace("foo listener worked");
}
dispatchEvent(new Event("foo"));
Jawnee
08-21-2008, 03:29 AM
Thanks again Mazoonist! :D You took the words right out of my mouth with the sentence "To illustrate that the process at its heart is really pretty simple..." because that's exactly how I feel. The concept is cool, easy, not a problem, but taking my understanding of that concept to the next level is proving to be challenging. It reminds me of when I was first introduced to the "variable" concept in math class when I was a kid.
The teacher said:
Ok kids, in this problem (2 + x = 5) what is x? x would have to be 3 because 2 plus 3 equals 5. Ok kids how about this problem (4 + x = 10) what does x equal?
Jawnee:
"x equals 3!!!"
Teacher:
No, in this case x equals 6 because 4 + 6 equals 10.
Jawnee: "What? Wait, you said x equals 3 before, now you say it's 6... how can it be both?!?!"
I was missing a fundamental understanding of what a variable really is and once I got it I felt so unbelievably silly. That's how I feel with Events in AS3.0. I'm sure once it clicks I'm going to feel quite silly. :D
Jawnee
08-21-2008, 09:31 PM
Anyone else have anything they can add to this? Is it possible to have a class send an event to another class if neither class contains they other. Or, to be more specific, is it even possible to have an external swf contained in a UILoader listen for events fired from the master swf that loads it? I've heard several people talk about firing events FROM the contained SWF to the master but not the other way around.
senocular
08-21-2008, 10:13 PM
Concerning events, its not usually a matter of "one class sending an event to another" but more about "one class listening for events from another" - and this is where the whole "listener" concept comes from. Events, when dispatched, have no destination; they aren't sent to other classes, they're just blindly tossed out into the wild. Its the responsibility of other objects to decide whether or not they deem it necessary to care whether or not its adventageous to listen to another object and any of the events it might be throwing out there with dispatchEvent.
Simple example: You have a tv object and a play button. The tv object has the ability to play video and wants to know when the play button is clicked so it can do exactly that. The video object would then listen to the play button for a click event. The button doesn't "send" events to the tv; it's just a buton. Its the responsibility of the tv to do the video playing to its the one handling the event - the event dispatched by the button once it fullfills its responsibility, being clicked.
Depending on what things do and what other objects may need to react to that determines what listens to what and what dispatches what. And for the most part dispatching should always be done from the current object (i.e. someObject.dispatchEvent is not a common use case). Other objects should decide to listen to that object if they need to react to its events.
When dealing with other SWFs, you would still need to be able to target certain objects between SWFs to listen for their events. There's 1 (and a half) exception, event bubbling. Bubbling events work their way of the parent hierarchy of a display list. A loaded SWF can dispatch bubbled events in a display object which will go through the containing display objects of the main SWF (that loaded the loaded SWF) until it reaches the SWF. The loaded SWF, however, cannot "bubble down" into loaded SWFs. A loaded SWF would need to get a reference of the object dispatching the event in the parent SWF in order to be able to listen for and react to its events.
The half refers to the sharedEvents object in the loaderInfo of the Loader/loaded SWF. This is a shared event dispatcher object that both SWFs can use to send events back and forth (and irregardless of security restrictions).
Jawnee
08-21-2008, 10:51 PM
Thanks for the great response senocular! I'll see if I can't utilize that information to get my two swfs communicating today! :D
Jawnee
08-24-2008, 10:05 AM
Ok so taking what you suggested Senocular I've been researching more online to try to get this to work. Here is where I currently stand:
I'm using a custom event class as follows:
package {
import flash.events.Event;
public class NavEvent extends Event {
public static const NAVCHANGE:String = "navchange";
public function NavEvent(type:String) {
super(type,true);
}
public override function clone():Event {
return new NavEvent(type);
}
}
}
I have two SWF files, one called index, the other movie. The index swf is the loader, it contains a UILoader component named movie with a source set to movie.swf. That makes the movie swf the loadee. In the index swf, when I need to notify the movie swf of a certain event I say:
this[curSection].loaderInfo.sharedEvents.dispatchEvent(new NavEvent(NavEvent.NAVCHANGE));
curSection = "movie" (it is the UILoader containing the swf of the same name. It is currently displayed and I need to let it know that I am navigating away so it can stop playing the streaming video it is displaying)
Inside the movie swf actionscript code I have a listener as such
addEventListener(NavEvent.NAVCHANGE, tester);
function tester(event:Event):void {
trace("THE RESULTS YOU WANTED!!!");
}
The trace never displays. Am I on the right track or am I totally misunderstanding the use of the loaderInfo.sharedEvents object? Thank you in advance for your continued support! I am extremely grateful for all your help.
senocular
08-24-2008, 01:43 PM
you're adding a listener to the main movie timeline in your second code snippet. That means it checks for NAVCHANGE events coming from that timeline instance. However, you never dispatch any event from that object. You're using the shared events object. Because of that, you need to have your movie swf add the listener to loaderInfo.shared events.
Jawnee
08-24-2008, 10:02 PM
It took a little extra searching but using what you last posted as a basis for my search I found a way to make it work. The major source of my confusion stemmed from the fact that I am using a UILoader component to load content swfs while most examples I found online were using the loader component. Here is the solution necessary to make communication between two swfs work when one loads the other inside a UILoader component.
The custom event class:
package {
import flash.events.Event;
public class NavEvent extends Event {
public static const NAVCHANGE:String = "navchange";
public function NavEvent(type:String) {
super(type,true);
}
public override function clone():Event {
return new NavEvent(type);
}
}
}
INSIDE your (loader swf)
myUILoader.content.loaderInfo.sharedEvents.dispatc hEvent(new NavEvent(NavEvent.NAVCHANGE));
INSIDE the swf to be loaded:
loaderInfo.sharedEvents.addEventListener(NavEvent. NAVCHANGE, tester);
function tester(event:Event):void {
trace("This is where I respond to the event!");
}
Reference article:
http://www.adobe.com/devnet/flashplayer/articles/secure_swf_apps_print.html
with this example:
//Parent.SWF
// The parent SWF registers an event listener that it will
// send to a child
loader:Loader = new Loader();
loader.load(new URLRequest("http://remote.com/remote.swf"));
addChild (loader);
stage.addEventListener(FocusEvent.FOCUS_IN, focusInListener);
//The Parent listener will forward the event to the remote child
function focusInListener (e:FocusEvent):void {
loader.contentLoaderInfo.sharedEvents.dispatchEven t(e);
}
//Remote.SWF
// The remote SWF registers to receive the event
loaderInfo.sharedEvents.addEventListener(FocusEven t.FOCUS_IN, focusInListener);
//The remote SWF does something with the event
function focusInListener (e:FocusEvent):void {
// Do something
}
Oh happy day! Thanks again senocular!
senocular
08-24-2008, 10:20 PM
Yeah, I dont think UILoader gives you access to the loaderInfo, which is lame, but that's part of what it does, makes things simpler.
|
vBulletin® v3.8.4, Copyright ©2000-2009, Jelsoft Enterprises Ltd.