簡體   English   中英

AS3:將 swf 加載為擴展 MovieClip 的自定義 Class - 獲取 null object 參考

[英]AS3: Loading swf as Custom Class that extends MovieClip - getting null object reference

我遵循上一個問題中的示例,我正在使用加載程序加載外部 swf,並且在加載程序事件處理程序內部我試圖將loader.content轉換為我的自定義 class PanelReferenceClip ,它擴展了MovieClip

當我發布時,我收到此錯誤:

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

只是為了確保並測試 swf 位置是否正確並且 swf 實際正在加載,我將內容的類型更改as MovieClip ,它工作正常。

編輯:我還想補充一點,這些 swf 存儲在本地,而不是通過互聯網、多個網絡或服務器拉取。

我不確定我是否在我的 class 中做了一些古怪的事情,所以我正在為我的自定義 class 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;
        }
    }

}

這是另一個 class 的一部分,它正在加載 swfs,然后嘗試將它們分配為 class 道具 _reference 類型為PanelReferenceClip 我這樣做是為了獲得 swf 及其子文件,因為當您導入 swf 時,您無法設置導入的 swf 的實例名稱。 因此,我為其分配了一個自定義 class 擴展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;
    }

所提供的事實並非決定性的,因為加載器 SWF 位於何處以及它從何處加載外部 SWF 非常重要。

我的猜測:您加載的內容位於不同的應用程序域中。 請參閱LoaderContext.applicationDomainLoader.load()方法的第二個參數。

關於機制的一些細節。 顯然,您將 PanelReferenceClip class 編譯成兩個不同的 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 

即使 output 相同,也不一定意味着類相同。 如果您使用某種調試器,您可以查看以下測試行中的 memory 地址。

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

正如@nox-noctis 所提到的,問題可能是由於應用程序域引起的。 在這里回答了一個類似的問題

問題的根本原因是實際上為PanelReferenceClip定義了兩個不同的類。 盡管它們可能具有相同的名稱,並且包含相同的代碼,但 Flash 將它們視為兩個不同的對象。

如果給類起兩個不同的名稱,這將更清楚地傳達類在 Flash 中的顯示方式。 本質上,您是在告訴 Flash 將一個 object 類型轉換為不同的類型,例如:

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

但是,將面板設置為MovieClip會起作用,因為面板確實擴展了 MovieClip。 這將允許您將其添加到顯示列表中,但不會為面板的實現提供 API。

一種解決方案是為類指定一個接口。 這告訴 Flash 即使代碼可能不同,這兩個類也可以以相同的方式使用。 這還有一個好處,就是只需要把接口編譯成父SWF,而不是整個class,減少了父文件大小。

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

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
{
    ...
}

然后父級將通過接口和已知的祖先引用加載的 class:

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

這可能是由於在無法進行類型轉換的類型之間進行轉換造成的。 執行someVariable as OtherClass時不會發生錯誤,變量將變為 null。 (顯示示例)

我會將原始影片剪輯的引用存儲為 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM