I am loading one swf into another using the Loader class, but when the child swf finishes loading and is added to the display list, its Document Class is not instantiated. I have a few trace statements that should execute when the object is created, but nothing is happening when loaded into the parent SWF. When I compile the child SWF on its own, the Document Class runs as expected.
So I'm wondering... how do I associate a child SWF's Document Class with Loader.content?
Code updated with a solution from Kishi below.
public class Preloader extends Sprite {
import flash.net.*;
import flash.display.*;
import flash.events.*;
// code in parent SWF's Document Class (Preloader.as)
private var swfLoader:Loader;
public var mainMovie:MovieClip;
public function Preloader(){
swfLoader = new Loader();
swfLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderDone);
swfLoader.load(new URLRequest("mainmovie.swf"));
}
private function loaderDone(e:Event):void {
// Cast Loader.content to MovieClip
mainMovie = MovieClip(swfLoader.content);
mainMovie.addEventListener(Event.ADDED_TO_STAGE, mainMovieAddedListener);
addChildAt(mainMovie, 0);
}
private function mainMovieAddedListener(e:Event):void {
// init() not necessary
}
}
// MainMovie.as runs after casting swfLoader.content to MovieClip
public class MainMovie extends Sprite {
public function MainMovie(){
trace('MainMovie WHATTUP');
}
public function init():void {
trace('init');
}
}
Cheers!
The problem is how you are trying to access the swf instance.
First, the document class instance is referred by Loader's content property. You'd reference it like this:
var swf:DisplayObject = swfLoader.content;
But, even then you'd have to cast the DisplayObject either to it's real class (MainMovie, in this case) or a dynamic class (such as MovieClip), as you are trying to use a custom property, that's not part of DisplayObject itself. Therefore, you could call MainMovie.init() like so:
var swf:MovieClip = MovieClip(swfLoader.content);
swf.init();
Hope that helps.
You're adding the mainMovie to stage, then you add a listener which inites it. The event is not fired as it's added after the movie is on stage.
What Cristi said is probably right, for the reason your init method isn't firing, but the more strange issue is that your loaded child swf's Constructor, MainMovie, should be firing as soon as that object is created.
Whenever I've done things like this, I've never created a new Sprite object from the contents of the loaded swf. Seems what you're doing there is using the swf like it's BitmapData, to create the Sprite mainMovie.
Try this: remove your statement that says swfLoader = null, and instead say addChild(swfLoader);. If you still want that event listener checking for it being added to the stage, put it before you do the addChild(swfLoader);, and of course put it on the swfLoader not the mainMovie object:
swfLoader.addEventListener(Event.ADDED_TO_STAGE, mainMovieAddedListener);
addChild(swfLoader);
See what you get then. [also can you paste the exact error you got from trying to access init?]
debu
Related
Note: Yes, I know that similar questions have been asked before. However, after following the answers in such questions I'm still stuck and can't find a solution to my problem.
I'm having a problem which requires adding DisplayObjects to the Flash stage. Since I have to Display elements of several different classes, I decided to create a class to work as an intermediary between the .as files and the addChild function called "Displayer" as shown below:
package
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.Stage;
public class Displayer extends Sprite //I read somewhere that DisplayObject
//as an extension can't be used for this, so Sprite will have to do.
{
private var _stage:Stage;
function Displayer()
{
_stage = new Stage;
}
public function displayElement(displayable:DisplayObject)
{
_stage.addChild(displayable);
}
}
}
I compile it and there appears a problem that I don't understand: Error #2012: Can't instantiate Stage class. Evidently, something in this code is either missing or out of place, but since it's so straightforward I fail to see what the problem can be. I'm sure that it's not very complicated, I probably just need an outsider's perspective.
The Stage object is not globally accessible. You need to access it through the stage property of a DisplayObject instance.
refer a following docs.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Stage.html
package
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.Stage;
public class Displayer extends Sprite
{
var isAddedToStage:Boolean;
public function Displayer()
{
if(stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event=null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
isAddedToStage = true;
}
public function displayElement(displayable:DisplayObject):void
{
if(isAddedToStage)
this.stage.addChild(displayable);
}
}
}
You don't instantiate the Stage class, as the error says. Just like you cannot instantiate the DisplayObject class (which is why you have to extend Sprite).
Basically, you have two options here:
1) You add the child from a DisplayObjectContainer instance.
var displayerInstance:Displayer = new Displayer();
this.addChild( displayerInstance );
You would run this from a DisplayObjectContainer object that has already been added to the global stage. There is only a single stage in every project, even if you embed SWFs, the stage property of the SWF is actually the stage property of the top level application. So if you have this Displayer instance nested inside a class which is nested inside another class that is created in your main application, you would have to run "addChild" in each of those classes to get the Displayer to show.
2) You cheat. This is not recommended, at all. Basically, you pass in the stage object of an object when you instantiate the Displayer class.
var displayerInstance:Displayer = new Displayer( this.stage );
public function Displayer( stage:Stage ) {
this.stage = stage;
if ( this.stage ) {
this.stage.addChild( this );
}
}
This is a method that is good for adding Singletons to the stage (except there is not constructor for a Singleton). I created a profiler just before Christmas that was a Singleton (And later found Scout, damnit) that used this method for adding things to the stage when appropriate.
Again, that second option is not recommended for this situation, but it is a possibility.
As an aside, you should never add things directly to Stage, unless there is a clear reason for doing so (such as a popup). You should follow the display list methodology, where a DisplayObjectContainer adds another DisplayObject or DisplayObject container as a child and so on and so forth so that they are all connected to the TopLevelApplication.
Ok, I think instantiating a stage class won't do because as the as3 documentation says: "The Stage object is not globally accessible. You need to access it through the stage property of a DisplayObject instance."
You should instead pass a reference to the Stage object to your Displayer class and you can get a reference to the stage object, as the docs say, via a display object instance.
So the constructor might now look like:
function Displayer( stage:Stage )
{
_stage = stage;
}
Assuming that the object which instantiates your Displayer is a child of the stage you can instantiate the Displayer by
displayer = new Displayer( stage );
If you use this approach there is no need for the Displayer class to extend anything or be added to the stage ( which is required btw in the approach of bitmapdata.com ).
There is always a simple solutions.if you need to add a child element into a stage from your class you can just pass the stage into your class as a object and add the child element into it, i did this for adding an image into my stage like this.
package {
import flash.display.Loader;
import flash.display.Sprite;
import flash.net.URLRequest;
import flash.display.Bitmap;
public class ImageLoader extends Sprite{
private var stageObj:Object; //create local variable to refarance stage
public function loadeimage(StageObject:Object, Url:String){ //StageObject will bring the stage refarance into the class
var reQuest:URLRequest = new URLRequest(Url);
loader.load(reQuest);
stageObj=StageObject; //make local refarance for stage inside the class
var image:Bitmap;
image=Bitmap(loader.content);
image.x = 100;
image.y = 100;
stageObj.addChild(image); // add whatever object into stage refarance and this means the real stage..
}
}
}
only the things with comments are important and you can save this file as ImageLoader.as and import it and use it like this.
import ImageLoader;
var IL:ImageLoader = new ImageLoader();
IL.loadeimage(this,"img.jpg");
its simple as that. i think this is what you have search for... good luck. (you can pass any container or parant container this or this.stage it doesn't matter your child will be a part of it.
this is a follow up question to this question
i have never actually got the flash-actionscript code execution order.
in flash pro i have an instance of a moveiclip on stage in frame one named tree1 and on frame 3 i have on the stage tree3.
in the document class i have this code:
stop();
var scaleFactor:Number = tree1.scaleX;
gotoAndStop(3);
tree3.scaleX = scaleFactor;
while this works when testing on the desktop, this app will go mobile at the end
is this the correct way to go or should i register for a frameComplete event before accessing instances on a certain frame
So, just dont use the Document Class cause you will need to declare all the scenes from the beginning and will be complicate to manage each scene.
I suggest you to instance a simple MovieClip linked with his own class like the example SceneTree, put it on each Keyframe. You will have more control when you are entering or exiting each frame.
package {
import flash.display.MovieClip;
import flash.events.Event;
public class SceneTree extends MovieClip {
public function SceneTree() {
super();
this.addEventListener(Event.ADDED_TO_STAGE, Init);
this.addEventListener(Event.REMOVED_FROM_STAGE, removed);
}
protected function Init (event:Event):void{
trace("added")
}
protected function removed (event:Event):void{
trace("removed")
}
}
}
waiting for Event.FRAME_CONSTRUCTED is the correct way whenever accessing assets on a timeline
it insures all assets have been created
Alright, so I'm trying to figure out when a child is added to a movieclip "x", and handling/detouring this operation from within this "x" movieclip.
I tried overriding addChild and addChildAt at with no prevail. The movieclips that are placed on the stage via flash still don't trigger addChild or addChildAt. However, tracing this.numChildren shows '2' correctly.
Any hints?
You can add an event listener for the "added" event for the x movie clip.
x.addEventListener(Event.ADDED, addHandler);
function addHandler(e:Event){
// your code here
}
This link may explain it better:
AS3.0 – Event.ADDED and Event.ADDED_TO_STAGE
The documentation is also a good resource:
flash.events.Event
You can override the default methods of a movieclip by doing the following:
Create a class to extend a movieclip:
package {
import flash.display.*;
public class SuperMovieClip extends MovieClip {
public function SuperMovieClip() {
// constructor code
super();
}
override public function addChild(child:DisplayObject):DisplayObject {
trace("Hello, I am overriding add child");
// still perform the default behavior but you can do what ever you want.
return super.addChild(child);
}
}
}
Then in Flash create a new movieclip, and make sure it is marked as Enable for ActionScript. The Class should be any name you want, but the base class needs to be SuperMovieClip (or the name you chose for your extended class) See image:
Now when any stage clip is created of this base type (regardless if it's in the IDE or through code) it will be of type SuperMovieClip and anytime addChild is called it will override the original function.
For example, I placed an instance of this mc from library onto the stage at design time and compiled it using the following code on the timeline:
import flash.display.Sprite;
stage_mc.addChild(new Sprite());
And it output Hello, I am overriding add child
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.
}
How do I access Stage Class properties in Costum Class?
Class:
package {
import Main;
import flash.events.*;
import flash.display.Sprite;
import flash.display.Stage;
public class Run extends Sprite {
var obj:a1_spr;
public function Run() {
runAssets();
}
private function runAssets():void {
obj = new a1_spr()
addChild(obj);
obj.x = stage.stageWidth/2;
}
}
}
Output:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
To expand on what Joel said, and put it into context:
Every display object has a .stage property, but that property is null until you add you display object onto the display list. So during construction, you will never be able to access it, (because it gets added afterwards)
The event ADDED_TO_STAGE gets fired when you add your object to the stage, ltting you know that the .stage property is now populated. After that happens you can access the stage from anywhere in you object.
Hope that clarifies things for you.
this.addEventListener(Event.ADDED_TO_STAGE, handleAdedToStage)
private function handleAddedToStage(event:Event):void
{
this.runAssets()
}
private function runAssets():void
{
obj = new a1_spr();
addChild(obj);
obj.x = this.stage.stageWidth/2;
}
You aren't going to have access to the stage in the constructor (unless you inject the stage into the class). Sprite has a stage property.
when flash compiles the fla assets with your .as files, there's no stage. so the code is initiated as preparation for your documentclass, you have to listen to if there's a stage so it can be rendered.
that's why you listen to ADDED_TO_STAGE , to check it's actually in the display list.
This problem occurs for all display objects, since they must be added to the display list when there's an actual stage.
get used to add that listener, and check for a stage. specially when working in a team and your doing your own components in a larger project.