AS3:将swf加载为扩展MovieClip的自定义类 - 获取空对象引用

时间:2011-08-16 05:41:56

标签: actionscript-3 casting

我跟着previous question的示例,我正在使用加载器加载外部swf,并在加载器事件处理程序中我试图将loader.content转换为我的自定义类PanelReferenceClip它扩展了MovieClip

发布时我收到此错误:

TypeError: Error #1009: Cannot access a property or method of a null object reference.

为了确保并测试swf位置是否正确并且实际上正在加载swf,我将内容的类型更改为as MovieClip并且它工作正常。

编辑:我还想补充一点,这些swfs存储在本地,而不是通过互联网,多个网络或服务器传输。

我不确定我是否在课堂上做了一些古怪的事情,所以我提供了自定义课程的来源PanelReferenceClip

package com.components 
{
    import com.UI.DevicePanel;
    import flash.display.MovieClip;

    /**
     * ...
     * 
     * used to store the loaded swf inside the panel
     * 
     * *parentPanel is set so that it is able to access it's parent Panel when needing
     * to set parameters.
     */

    public class PanelReferenceClip extends MovieClip 
    {
        private var _parentPanel:DevicePanel;
        private var _bg_mc:MovieClip;
        private var _oldY:Number = 0;
        private var _oldX:Number = 0;
        private var _IsDragging:Boolean = false;

        public function PanelReferenceClip() {
            super();
        }

        /*--------------------------------------------------------------------------
         * GETTERS AND SETTERS
         * -----------------------------------------------------------------------*/

        public function set parentPanel(p:DevicePanel):void {
            _parentPanel = p;
        }

        public function get parentPanel():DevicePanel {
            return _parentPanel;
        }

        public function get bg_mc():MovieClip {
            try {
                return getChildByName("bg_mc") as MovieClip;
            } catch (e:Error) {
                trace("could not find bg_mc in " + _parentPanel.DeviceName + " panel");
            }

            return null;
        }

        public function set oldY(n:Number):void {
            _oldY = n;
        }

        public function get oldY():Number {
            return _oldY;
        }

        public function set oldX(n:Number):void {
            _oldX = n;
        }

        public function get oldX():Number {
            return _oldX;
        }

        public function set IsDragging(b:Boolean):void {
            _IsDragging = b;
        }

        public function get IsDragging():Boolean {
            return _IsDragging;
        }
    }

}

这是另一个类的一部分,它正在加载swfs,然后尝试将它们分配为类PanelReferenceClip的类prop _reference。我这样做是因为我能够得到swf和它的孩子,因为当你导入swf时你不能设置导入的swf的实例名称。所以我正在为它分配一个扩展MovieClip的自定义类,以便我可以存储一些自定义属性。

        private function handleLoad(e:Event):void
        {
            e.target.removeEventListener(Event.COMPLETE, handleLoad, false);
            // keep reference to the content
            _reference = e.target.content as PanelReferenceClip;
                    //  ** BREAKS ON THE NEXT LINE **/
            trace(_reference.numChildren);

            // add loader to the display list so we can see the external SWF.
            addChild(e.target.loader);

            // signal the sim engine that the swf has loaded 
            // and to go ahead and wire up the components
            dispatchEvent(new DataEvent(DataEvent.COMPLETE));

            initPanel();
        }

以下是用于加载swf的方法。我在应用程序上下文部分添加了尝试,但我仍然没有到达任何地方。

    public function loadSWF(theSWF:String):void
    {
        var url:String = theSWF;
        var urlReq:URLRequest = new URLRequest(url);
        _urlError = url;
        _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoad);
        _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
        _loader.load(urlReq,context);
        _loader.mouseEnabled = false;
    }

3 个答案:

答案 0 :(得分:1)

所提供的事实并不是决定性的,因为加载器SWF和加载外部SWF的位置非常重要。

我的猜测:您加载的内容位于不同的ApplicationDomain中。请参阅LoaderContext.applicationDomain和第二个参数Loader.load()方法。

关于力学的一些细节。显然,您将PanelReferenceClip类编译为两个不同的SWF文件。当您加载一些带有代码的外部SWF文件时,VM根据您提供的加载器上下文决定是否将任何传入声明与加载器SWF的混合声明混合。如果您指定的上下文允许混合,则传入的SWF使用与父SWF具有的重合限定名称相同的声明。如果不是 - 即使它们的完全限定名称相同,初始化的类也是不同的。在后一种情况下,您将无法将加载的内容转换为您喜欢的内容。

handleLoad()方法中尝试以下测试:

trace(getQualifiedClassName(e.target.content)); // This is the incoming declaration 
trace(getQualifiedClassName(PanelReferenceClip)); // This is the parent declaration 

即使输出相同,也不一定意味着类是相同的。如果您使用某种调试器,可以查看以下测试行中的内存地址。

var incomingClass:Class = e.target.content["constructor"];
var residentClass:Class = PanelReferenceClip;
trace(incomingClass == residentClass); // toggle breakpoint here and compare memory addresses

答案 1 :(得分:1)

如@nox-noctis所述,问题可能是由于applicationDomain引起的。我回答了similar question here

问题的根本原因是实际上为PanelReferenceClip定义了两个不同的类。尽管他们可能具有相同的名称,并且包含相同的代码,但Flash将它们视为两个不同的对象。

如果你给这两个不同的名字,这将更清楚地表达这些类在Flash中的表现方式。本质上,您告诉Flash将一种对象类型转换为其他类型,例如:

var foo:Foo = new Foo();
var bar:Bar = foo as Bar(); // where Bar does not inherit Foo

虽然可以使用的是将面板设置为MovieClip,因为面板确实扩展了MovieClip。这样可以将它添加到显示列表中,但不会为面板的实现提供API。

一种解决方案是为类指定接口。这告诉Flash即使代码可能不同,这两个类也可以以相同的方式使用。这样做的好处是,您只需要将接口编译到父SWF中,而不是整个类,从而减少父文件的大小。

面板的界面可能如下所示:

public interface IPanelReference
{
    function set parentPanel(p:IDevicePanel):void;
    function get parentPanel():IDevicePanel;
    function get bg_mc():MovieClip;
    function set oldY(n:Number):void;
    function get oldY():Number;
    function set oldX(n:Number):void;
    function get oldX():Number;
    function set IsDragging(b:Boolean):void;
    function get IsDragging():Boolean;
}

将界面应用于面板,如下所示:

public class PanelReferenceClip extends MovieClip implements IPanelReference
{
    ...
}

然后父节点将通过接口和已知的祖先引用加载的类:

private function handleLoad(e:Event):void
{
    ...
    _reference = e.target.content as IPanelReference;
    trace((_reference as DisplayObjectContainer).numChildren);
    trace(_reference.oldX);
    addChild(_reference as DisplayObject);
    ....
}

答案 2 :(得分:1)

这可能是由无法进行类型转换的类型之间的转换引起的。该变量将变为null,而不是在执行someVariable as OtherClass时发生错误。 (示例)

我会将对原始movieclip的引用存储为PanelReferenceClip中的某个属性,并在需要访问.numChildren

之类的内容时使用该引用
//MovieClips are subclasses of Sprites... this conversion works
var originalMC:MovieClip = new MovieClip();
var sprite:Sprite = originalMC as Sprite; 
trace("sprite: " + sprite); //defined

//Sprites are not subclasses of MovieClips... this conversion wont work
var originalSprite:Sprite = new Sprite();
var mc:MovieClip = originalSprite as MovieClip; 
trace("mc: " + mc); //null

//MovieClips are not subclasses of PanelReferenceClips (quite the opposite)
//this conversion wont work, just as the one before
var panelRef:PanelReferenceClip = originalMC as PanelReferenceClip;
trace("panelRef: " + panelRef); //null