0

我正在用 Actionscript 2 编写一个应用程序,它需要访问 XML 文件、获取其内容,然后使用该信息来填充一堆文本字段。这在基于帧的动作脚本中很简单,因为我所要做的就是停止做事,将下一步设置为从 XML.onLoad 函数运行,然后我就可以开始了。但是我正在用类文件重写这个东西,并且在调用依赖于它已经存在的函数之前,我很难让我的 XML 加载。

详细说明,

假设我有一堂课,Main。该类看起来像:

import XMLItems;

class Main {
    private var _xmlItems:XMLItems;

    public function Main() {
        _xmlItems = new XMLItems();
        _itemData = _xmlItems.getItemData();
}

到目前为止看起来还不错,对吧?它所做的只是创建一个新的 XMLItems 实例,然后调用该实例的 getItemData() 方法。

所以现在这里是 XMLItems:

import mx.xpath.XPathAPI;
import XMLLoader;

class XMLItems {

    private var _loader:XMLLoader;
    private var _itemData:XML;
    private var _eventPath:String = "/*/dbresult/playlist/";

    public function XMLItems() {
        trace('XMLItems object instantiated.');
    }

    private function parseItemData(itemData:XML):Array {
        var nodes:Array = XPathAPI.selectNodeList(itemData, _eventPath + "item");

        return nodes;
    }

    public function getItemData():Array {
        _loader = new XMLLoader();
        _itemData = _loader.getXML();
        var r:Array = parseItemData(_itemData);
        return r;
    }

}

所以现在我们有了一个 XMLLoader 对象,以便我们可以调用该 getXML 函数(别担心,最后一个):

import mx.xpath.XPathAPI;

class XMLLoader {

    private var _rawXML:XML;
    private var _xmlLocation:String = '';
    private var _recommendRequestURL:String;
    private var _xmlIsLoaded:Boolean = false;

    private var _eventPath:String = "/*/dbresult/playlist/";

    public function XMLLoader() {
        trace('Instantiating XML loader...');
        _recommendRequestURL = _root.recmReqUrl ? _root.recmReqUrl : escape('http://www.myURL.com/');

        _rawXML = new XML();
        _rawXML.ignoreWhite = true;

        var host = this;
        _rawXML.onLoad = function(success:Boolean) {
            if (success) {
                _xmlIsLoaded = true;
                trace('XML data was successfully loaded.');
                var nodes:Array = XPathAPI.selectNodeList(host._rawXML, host._eventPath + "item");
                trace("Number of XML nodes: " + nodes.length);
                trace("Sample node: " + nodes[nodes.length - 1]);
            } else {
                trace('There was an error loading the XML. Please check the XML file.');
            }
        }

        loadXML();
    }

    private function loadXML():Void {
        var xmlLocation:String;
        if ( !_root.recmReqUrl ) {
            trace("There is no network address for the XML file. Defaulting to local settings.")
        xmlLocation = './localBannerData.xml';
        } else {
            xmlLocation = _recommendRequestURL.concat( '&spec=', 'specInfo', 'getCatInfo()', 'getXCatInfo()', '&t=', new Date().getTime() );
        }

        if ( _rawXML.load( xmlLocation ) ) {
            trace('Loading XML file: ' + xmlLocation);
        } else {
                trace('I\'m having difficulty finding the XML. You might want to check your data source.')
        }

    }

    public function getXML():XML {
        return _rawXML;
    }

}

问题是当调用 getXML 函数时,XML 还没有加载。所以该方法将一个空的 XML 对象返回给 XMLItems 类,即使我这样做了:

_loader = new XMLLoader();
_itemData = _loader.getXML();

尽管我的意思是构造函数必须在评估下一条语句之前完成运行。显然它没有。

我尝试创建一个由 onLoad 函数设置的布尔值,并在 getXML 方法中(在方法返回之前)放置一个 while (true) 循环,以检查该布尔值并在它返回 true 时中断,但它从未返回 true,所以它只是永远循环。

我真的不明白这里发生了什么。为什么我不能让班级同步请求 XML 并在 XML 进来之前冷静下来?

谢谢,SS

4

1 回答 1

2

Hrm,有人可能会纠正我,但我认为flash 不能同步加载外部资源(xml 或其他),我相信这可能是设计使然。至少根据我的经验,每当加载外部资源时,它总是异步完成的。

特别是对于 AS2 XML,它还说 load() 方法调用是异步的:

加载过程是异步的;它不会在 load() 方法执行后立即完成。 http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=Part2_AS2_LangRef_1.html

您可能已经知道这意味着什么:您必须添加一个回调方法或让您的 xml 加载类抛出您要监听的某种“完成”事件(对于这个问题,表面上或多或少是相同的事情我认为)。


如评论中所述添加到答案

正如我在评论中提到的,您可以允许您的类通过使用 EventDispatcher 类来引发自定义事件。我猜这个类实际上是用于从 flash mx 组件调度事件,这就是它在 mx 包中的原因。

因为您需要为事件对象创建自己的事件类。从文档/谷歌搜索中,(不记得确切的位置),在基本级别,事件具有两个属性:目标(对引发事件的对象的引用)和名称。如果您想要自己的自定义事件,您可以随时添加更多或扩展它。

class Event {
    public var type:String;
    public var target:Object;

    public function Event(inputType:String, inputTarget:Object){
        type = inputType;
        target = inputTarget;
    }
}

在这里进行了仔细检查,看来您实际上不需要创建自己的 Event 类,它可以只是具有这两个必需属性的通用对象-但我还是喜欢制作一个以根据需要重用并保持我的代码更干净.

然后在您的(XML)类中:

// add this
import mx.events.EventDispatcher;

class MyXMLClass {

    public MyXMLClass(){        
        // add this to your constructor
        mx.events.EventDispatcher.initialize(this);
    }

    // add these, leave these empty, they are init'ed during runtime
    private function dispatchEvent() {};
    public function addEventListener() {};
    public function removeEventListener() {};

} // end of class

现在这为您的课程添加了 3 个方法。在您的类方法中,您可以像这样发送事件:

// first create the event object
var event:Event = new Event("complete", this); // 'this' refers to this current object
dispatchEvent(event);

在您的班级之外,您可以像这样注册一个事件侦听器:

var xmlObject:MyXMLClass = new MyXMLClass();

function eventListenerMethod(inputEvent:Event):Void {
    trace("I got the event: "+ inputEvent.type +" from "+ inputEvent.target);
}
xmlObject.addEventListener("complete", eventListenerMethod);

现在,由于事件名称“完成”是一个常量字符串并且不应更改,我建议在事件类中将其设为静态常量,如下所示:

class Event {
    // added this
    public static var COMPLETE:String = "complete";

    public var type:String;
    public var target:Object;

    public function Event(inputType:String, inputTarget:Object){
        type = inputType;
        target = inputTarget;
    }
}

因此,您可以通过 Event.COMPLETE 来引用它,例如:

new Event(Event.COMPLETE, this);
addEventListener(Event.COMPLETE, eventListenerMethod);

只是为了减少拼写错误的机会,并且可以通过查看事件类来引用您拥有的事件类型。

现在还有一件事要担心:我发现事件处理程序方法在引发事件的对象范围内运行。您可以通过添加 trace(this); 来验证这一点。到 eventListenerMethod() 方法,它应该追踪你的类,而不是该方法的编写位置。为了解决这个问题,您可以使用 Delegate 类使该方法在编写它的范围内运行。

所以之前:

// assuming this is written in the timeline/frame
function eventListenerMethod(inputEvent:Event):Void {
    trace("I got the event: "+ inputEvent.type +" from "+ inputEvent.target);
    trace(this); // will trace out "xmlObject" instead of _level0 or _root as you might expect
}
xmlObject.addEventListener(Event.COMPLETE, eventListenerMethod);

你可以走了:

import mx.utils.Delegate;

function eventListenerMethod(inputEvent:Event):Void {
    trace("I got the event: "+ inputEvent.type +" from "+ inputEvent.target);
    trace(this); // should trace out _level0 / _root now or whatever 'this' was refering to in the Delegate.create() method
}
xmlObject.addEventListener(Event.COMPLETE, Delegate.create(this, eventListenerMethod));

在我和你一样为加载外部数据和遇到随机范围问题而困惑之前,我不知道如何正确修复它们(必须想出“创造性”方法)。=b

当我不得不在 AS2 中工作几年时,发现这两件事让我保持清醒一点,这两件事在 AS3 中做得更好(例如,不需要范围问题/委托,还有很多对象自然已经扩展了 EventDispatcher 类,使其更易于使用或更“原生”,正如我喜欢说的那样,它有一个可以使用/扩展的实际 Event 类,因此您不需要自己制作)。

请去尝试我写的后半部分,看看你是否真的需要使用 Delegate 类,因为你可能并不总是需要它,并且当你需要删除事件侦听器时,有时会导致垃圾收集本身的问题(在在这种情况下,我需要保留对传递给 addEventListener() 的 Delegate 对象的显式引用,以便稍后将其传递给 removeEventListener()。根据我的经验,我几乎总是需要它,这就是我添加它的原因对我的回答。我发现它对其他事情也很有用。您可以单独使用它来运行其他对象范围内的方法(在这种情况下,它用于在_root/“写在哪里”)。

我强烈建议尽可能迁移到 AS3。我还建议在帮助中查找 EventDispatcher 和 Delegate 类的引用,以便更好地了解内部发生的情况 - 我已经很长时间没有编写任何真正的 AS2 并且我的记忆可能有点模糊。我刚才写的大部分都是凭记忆写的。:)

于 2013-01-27T11:06:06.270 回答