开发者

In AS3, can a library symbol extend another library symbol, assuming each are linked to a class?

开发者 https://www.devze.com 2023-01-30 08:12 出处:网络
For example: Library symbol \"Card\" is linked to class \"Card\" which extends \"MovieClip\".Library symbol \"Card\" contains a card background image.

For example:

  1. Library symbol "Card" is linked to class "Card" which extends "MovieClip". Library symbol "Card" contains a card background image.
  2. Library symbol "Ace" is linked to class "Ace", which extends class "Card". Library symbol "Ace" contains a TextField with a big letter "A".

So we have Ace extends Card which extends MovieClip. Ace therefore extends MovieClip, but does not DIRECTLY extend MovieClip.

When I drop an instance of Ace on the stage and compile the clip, all that shows up is the big letter A. However, I expected the background image from Card to be included, since Ace extends Card, and the Card symbol contains the background.

It seems like Flash ignores symbol content unless it belongs to the top-level class being instantiated. I think it's LAME that one symbol can't extend another. The IDE could easily draw Card as a non-editable background开发者_运维百科 while I'm editing Ace which extends it, and it should instantiate Card's content and then Ace's content when an Ace is instantiated. Thoughts?


Yeah, I've tried to do that. In theory you'd expect the display list of each extended class to stack, but they don't - it works as you describe, where you only see the graphics associated with the most recent class.

It's not a deal-breaker for you, though - various architectural options are at your disposal. You could, for instance, create a CardBackground class which is exported out of your library and has the card shape etc. Then you create a Card class which has a background:CardBackground property. Then your Ace class can extend Card and it should have the desired background.

Technically you're supposed to favor composition over inheritance, but Flash really encourages the inheritance mindset. Once you get used to breaking out of that you'll realize it's possible to create much more powerful, robust classes using composition in the manner described.

Cheers, and I hope that helps!


The base class to your card_mc(Card movieclip) may be your Card class but it doesn't make your Card class synonymous with card_mc.

Try doing this instead:

1) Create a movieclip containing the card background image and call it cardSprite_mc. Give it the class name CardSprite and set its base class to flash.display.Sprite.

2) Create a movieclip containing the textfield containing the letter "A" and call it ace_mc. Give it the class name Ace and a base class of com.cards.Ace.

3) Create a class called Card with the following code:

package com.cards
{
    import flash.display.Sprite;

    public class Card extends Sprite
    {
        public function Card():void 
        {
            addChildAt(new CardSprite(), numChildren - 1);

        }// end function

    }// end class

}// end package

4) Create a class called Ace with the following code:

package com.cards
{
    import com.cards.Card;

    public class Ace extends Card
    {
        public function Ace():void 
        {

        }// end function

    }// end class

}// end package

Now if you add an instance of Ace to the stage you should see the card background image too.

I hope this helped :)


You can't do this in a programmatic way.

Instead you have to use the Flash Authoring environment. Extend symbols by creating one which includes the base symbol inside it.

And, yes I agree, it's quite LAME.


Taurayi's solution is inspiring, because it establishes that missing explicit link from Class to Symbol, ensuring the Symbol's content is instantiated whether it's the top-level class or just a base class in an inheritance chain. A side effect of that approach, however, is that it adds an extra containment level in Card's content, namely the CardSprite container.

I have managed to implement a practical generic solution that actually preserves the expected stacking behavior of all inherited symbols. For example, if you check "numChildren" on an instance of Symbol2 below, it will be exactly the sum of Symbol1.numChildren and Symbol2.numChildren, so it's a true merge stacking of symbol content.

When your symbol is in an inheritance chain, simply add this "ensureLinkage" call anytime after a call to the super() method.

package
{
    public class Symbol1 extends Sprite
    {
        public function Symbol1()
        {
            super();
            BugFixes.ensureLinkage( this, "Symbol1" );
        }
    }
}


package
{
    public class Symbol2 extends Symbol1
    {
        public function Symbol2()
        {
            super();
            BugFixes.ensureLinkage( this, "Symbol2" );
        }
    }
}

Note: Don't forget to make sure your top-level symbol also explicitly defines a class with the above pattern.

When Symbol2 and Symbol1 are linked to corresponding symbols in the library, their content will now stack. Just drop an instance of Symbol2 on the stage, and test the movie. You'll see that Symbol1's content appears under Symbol2's content. (Note: does not appear in the designer, since this is a runtime fix).

The implementation of ensureLinkage is as follows:

package
{
    import flash.utils.getQualifiedClassName;
    import flash.utils.getDefinitionByName;
    import flash.events.Event;

    public class BugFixes
    {

        public static var linkageMonitor:Object = new Object();
        private static var linkageMonitorAuthority:Array = new Array();

        public function BugFixes()
        {
        }

        public static function ensureLinkage( instance:*, className:String )
        {
            if (getQualifiedClassName( instance ) != className) //detect non-top-level construction
            {
                //prevent inevitable factorial-recursive construction
                var stack:Array = linkageMonitor[instance] as Array;
                if (stack == null)
                {
                    stack = new Array();
                    stack["numChildren"] = instance.numChildren;
                    linkageMonitor[instance] = stack;
                }

                var barredByAuthority:Boolean = false;
                if (linkageMonitorAuthority.length > 0)
                    barredByAuthority = (linkageMonitorAuthority[linkageMonitorAuthority.length - 1] as Array).indexOf( className ) > -1;
                if (stack.indexOf( className ) == -1 && !barredByAuthority)
                {
                    stack.push( className ); //remember construction
                    trace( "ensuring Linkage of inherited class " + className );

                    //perform top-level construction to trigger symbol linkage and child object instantiation
                    linkageMonitorAuthority.push( stack );
                    var temp:* = new (getDefinitionByName( className ) as Class)();
                    linkageMonitorAuthority.pop();

                    //Merge children
                    while (temp.numChildren > 0)
                        instance.addChild( temp.getChildAt( 0 ) );

                    //Merge properties
                    for (var prop:String in temp)
                        instance[prop] = temp[prop];
                }
                else
                {
                    trace( "skipping redundant construction of: " + className );
                }
            }
            else
            {
                var stack:Array = linkageMonitor[instance] as Array;
                if (stack != null)
                {
                    var nc:int = int(stack["numChildren"]);
                    trace("construction completing for " + getQualifiedClassName( instance ) );
                    for (var i:int = 0; i < nc; i++)
                        instance.setChildIndex( instance.getChildAt( 0 ), instance.numChildren - 1 );
                }
                delete linkageMonitor[instance]; //top-level constructor is completing, all relevant sub-objects have been constructed
            }
        }
    }
}

Basically, it detects whether symbols are going to need manually instantiated, by seeing whether the qualified class name of the instance matches the expected class name passed to the call from the class itself. Since it's called after "super", the calls start at the deepest class and ensure its library symbol's children are instantiated by making a temporary top-level instance and claiming its children as its own. The very first call for the instance also grabs the original number of children present, since the top-level clip in the stack will have already instantiated its children before any constructor code is run at all. By storing that number, a final step can then pull those initial children to the top where they belong. The class ensures no unnecessary recursion takes place, by using an "authority" stack to ensure the main stack is always visible to child constructors.

One issue is that static strokes are not persisted, but that is only because AS3 provides no API for accessing strokes (i.e. once you draw a line in the designer, or with graphics.lineTo, there is no way to programatically access that stroke for enumeration or modification purposes, except to clear all strokes at once). So that's not a limitation of this approach, but rather Flash's API.

Perhaps Adobe was simply unable to come up with this implementation :P

Please note that if your symbols do any work that ties the symbol instance to other code, there could be an issue, since this class claims ownership of children from a temporary instance. It also claims the values of variable references from the temporary instance using a for loop, but that's the best it can do in a generic implementation such as this.

0

精彩评论

暂无评论...
验证码 换一张
取 消