views:

9612

answers:

1

I have a helper method has been created which allows a MovieClip-based class in code and have the constructor called. Unfortunately the solution is not complete because the MovieClip callback onLoad() is never called.

(Link to the Flashdevelop thread which created the method .)

How can the following function be modified so both the constructor and onLoad() is properly called.

    //------------------------------------------------------------------------
    //  - Helper to create a strongly typed class that subclasses MovieClip.
    //  - You do not use "new" when calling as it is done internally.
    //  - The syntax requires the caller to cast to the specific type since 
    //    the return type is an object. (See example below).
    //
    //  classRef, Class to create 
    //  id,       Instance name
    //  ...,      (optional) Arguments to pass to MovieClip constructor
    //  RETURNS   Reference to the created object 
    //  
    //  e.g., var f:Foo = Foo( newClassMC(Foo, "foo1") );
    //
    public function newClassMC( classRef:Function, id:String ):Object 
    {
      var mc:MovieClip = this.createEmptyMovieClip(id, this.getNextHighestDepth()); 
      mc.__proto__ = classRef.prototype; 

      if (arguments.length > 2) 
      {
        // Duplicate only the arguments to be passed to the constructor of
        // the movie clip we are constructing.
        var a:Array = new Array(arguments.length - 2);
        for (var i:Number = 2; i < arguments.length; i++)
          a[Number(i) - 2] = arguments[Number(i)];

        classRef.apply(mc, a);
      }
      else
      {
        classRef.apply(mc);
      }

      return mc; 
    }

An example of a class that I may want to create:

class Foo extends MovieClip

And some examples of how I would currently create the class in code:

// The way I most commonly create one:
var f:Foo = Foo( newClassMC(Foo, "foo1") );

// Another example...
var obj:Object  = newClassMC(Foo, "foo2") );
var myFoo:Foo   = Foo( obj );
+1  A: 

Do I understand correctly that you want to create an instance of an empty movie clip with class behavior attached and without having to define an empty clip symbol in the library?

If that's the case you need to use the packages trick. This is my base class (called View) that I've been using over the years and on hundreds of projects:

import mx.events.EventDispatcher;

class com.tequila.common.View extends MovieClip
{
    private static var _symbolClass : Function = View;
    private static var _symbolPackage : String = "__Packages.com.tequila.common.View";

    public var dispatchEvent : Function;
    public var addEventListener : Function;
    public var removeEventListener : Function;

    private function View()
    {
     super();

     EventDispatcher.initialize( this );

     onEnterFrame = __$_init;
    }

    private function onInitialize() : Void
    {
         // called on the first frame. Event dispatchers are
         // ready and initialized at this point.
    }

    private function __$_init() : Void
    {
     delete onEnterFrame;

     onInitialize();
    }

    private static function createInstance(symbolClass, parent : View, instance : String, depth : Number, init : Object) : MovieClip
    {
     if( symbolClass._symbolPackage.indexOf("__Packages") >= 0 )
     {
      Object.registerClass(symbolClass._symbolPackage, symbolClass);
     }

     if( depth == undefined )
     {
      depth = parent.getNextHighestDepth();
     }

     if( instance == undefined )
     {
      instance = "__$_" + depth;
     }

     return( parent.attachMovie(symbolClass._symbolPackage, instance, depth, init) );
    }

    public static function create(parent : View, instance : String, depth : Number, init : Object) : View
    {
     return( View( createInstance(_symbolClass, parent, instance, depth, init) ) );
    }
}

So, all you have to do to use this class is to subclass it:

class Foo extends View
{
    private static var _symbolClass : Function = Foo;
    private static var _symbolPackage : String = "__Packages.Foo";

    private function Foo()
    {
       // constructor private
    }

    private function onInitialize() : Void
    {
         // implement this to add listeners etc.
    }

    public static function create(parent : View, instance : String, depth : Number, init : Object) : Foo
    {
     return( Foo( createInstance(_symbolClass, parent, instance, depth, init) ) );
    }
}

You can now create an instance of Foo like this;

var foo : Foo = Foo.create( this );

Assuming that 'this' is some type of MovieClip or View.

If you need to use this with a library symbol then just leave out the __Packages prefix on the _symbolPackage member.

Hope this helps...

Luke
Thanks for the interesting AS2 trickery! Different problem though. A MovieClip defined in the library is set to a class. At runtime I want to add instances of that class to the stage and be able to pass args to the ctor. My newClassMC() function calls the ctor but onLoad() doesn't occur.
tronster
You simply can't do that with Movieclips unless you use composition instead of inheritance.
Luke
Luke, I still think it's possible by forcing an onLoad in the proto (or contructor) chain; it's just complicated enough to make my head spin. If it isn't possible, I'd love to know why.
tronster
What I mean is that when you create an instance of a MovieClip you do not use the 'new' operator (which triggers the constructor), you'd use attachMovie instead. So, there is no way to pass parameters to the constructor. I suggest you use an init function of some sort to initialize your clip.
Luke