AS3: загрузка SWF-файла в качестве пользовательского класса, расширяющего MovieClip - получение нулевой ссылки на объект - PullRequest
1 голос
/ 16 августа 2011

Я последовал примеру из предыдущего вопроса , и я загружаю внешний SWF-файл, используя загрузчик, и внутри обработчика событий загрузчика. Я пытаюсь привести loader.content в качестве своего пользовательского класса PanelReferenceClip, который расширяет MovieClip

При публикации я получаю эту ошибку:

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

Просто для того, чтобы проверить и проверить правильность расположения swf и загрузку swf, я изменил тип содержимого на as MovieClip, и он работал нормально.

РЕДАКТИРОВАТЬ: Я также хотел добавить, что эти SWF-файлы хранятся локально, а не через Интернет, несколько сетей или серверов.

Я не уверен, что сделал что-то необычное в своем классе, поэтому я предоставляю исходный код для своего пользовательского класса 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;
        }
    }

}

Здесь часть другого класса, которая загружает SWF-файлы и затем пытается присвоить им класс prop_reference типа PanelReferenceClip. Я делаю это, поэтому я могу получить доступ к 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 ]

1 голос
/ 16 августа 2011

Как уже упоминалось @ nox-noctis, проблема может быть связана с доменом приложения.Я ответил на похожий вопрос здесь .

Основной причиной проблемы является то, что на самом деле для 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);
    ....
}
1 голос
/ 16 августа 2011

Это может быть вызвано конвертацией между типами, которые не могут быть Typecast. Вместо ошибки, возникающей при выполнении someVariable as OtherClass, переменная станет нулевой. (Показанный пример)

Я бы сохранил ссылку на оригинальный мувиклип как одно из свойств в 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
1 голос
/ 16 августа 2011

Приведенные факты не являются окончательными, так как очень важно, где находится SWF-загрузчик и откуда он загружает внешние SWF-файлы.

Мое предположение: ваш загруженный контент находится в другом домене приложения. См. LoaderContext.applicationDomain и второй параметр для Loader.load () .

Некоторые подробности о механике. По-видимому, вы компилируете класс PanelReferenceClip в два разных SWF-файла. Когда вы загружаете некоторый внешний SWF-файл с кодом в нем, ВМ решает, смешивать или нет какие-либо входящие объявления с декларациями 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...