简体   繁体   English

如何使Flash等到加载XML以使函数可用于从另一个类调用?

[英]How do I get Flash to wait until XML is loaded to make a function available for calling from another class?

I am writing an application in Actionscript 2 that needs to access an XML file, get its contents, and then use that information to populate a bunch of text fields. 我在Actionscript 2中编写了一个应用程序,它需要访问XML文件,获取其内容,然后使用该信息填充一堆文本字段。 This was straightforward in frame-based actionscript, because all I had to do was stop doing stuff, set the next step to run from the XML.onLoad function and I was good to go. 这在基于框架的actionscript中很简单,因为我所要做的就是停止做东西,设置下一步从XML.onLoad函数运行,我很高兴。 But I'm rewriting this thing with class files and I'm having a hard time getting my XML to load in before the function is called that relies on it being in already. 但我正在用类文件重写这个东西,我很难在调用函数之前加载我的XML,而函数依赖于它已经存在。

To elaborate, 详细说明,

Let's say I have a class, Main. 假设我有一个班级,主要。 That class looks like: 那个班看起来像:

import XMLItems;

class Main {
    private var _xmlItems:XMLItems;

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

Looks OK so far, right? 到目前为止看起来不错吧? All it does is create a new XMLItems instance, and then call that instance's getItemData() method. 它所做的只是创建一个新的XMLItems实例,然后调用该实例的getItemData()方法。

So now here's XMLItems: 所以现在这里是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;
    }

}

So now we have an XMLLoader object to make so that we can call that getXML function (don't worry, last one): 所以现在我们有一个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;
    }

}

The problem is that when the getXML function is called, the XML hasn't loaded yet. 问题是,当调用getXML函数时,XML尚未加载。 So the method is returning an empty XML object to the XMLItems class, even though I did this: 所以该方法将一个空的XML对象返回给XMLItems类,即使我这样做了:

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

which I though meant that the constructor had to finish running before the next statement was evaluated. 我认为构造函数必须在评估下一个语句之前完成运行。 Apparently it does not. 显然它没有。

I've tried making a boolean that is set by the onLoad function and putting a while (true) loop in the getXML method (before the method returned) that checks against that boolean and breaks if it returns true, but it never returned true, so it just looped forever. 我已经尝试制作一个由onLoad函数设置的布尔值,并在getXML方法中放置一个while(true)循环(在返回的方法之前),检查该布尔值并在它返回true时中断,但它从未返回true,所以它只是永远循环。

I really don't understand what is happening here. 我真的不明白这里发生了什么。 Why can't I get the class to maybe request the XML synchronously and just chill until the XML comes in? 为什么我不能让类同步地请求XML并且直到XML进入才冷却?

Thanks, SS 谢谢,SS

Hrm, someone may correct me on this, but I don't think flash can load external resources (xml or otherwise) synchronously and I believe this may be by design. 嗯,有人可能会纠正我,但我不认为闪存可以同步加载外部资源(xml或其他),我相信这可能是设计的。 At least In my experience, whenever external resources are loaded it is always done asynchronously. 至少根据我的经验,无论何时加载外部资源,它总是异步完成。

For AS2 XML specifically, it also says load() method calls are asynchronous: 对于AS2 XML,它还说load()方法调用是异步的:

The load process is asynchronous; 加载过程是异步的; it does not finish immediately after the load() method is executed. 执行load()方法后,它不会立即完成。 http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=Part2_AS2_LangRef_1.html http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=Part2_AS2_LangRef_1.html

You probably already know what this means: you'll have to add a call back method or have your xml loading class throw some sort of 'complete' event you'd listen for (more or less the same thing on the surface for this problem I think). 您可能已经知道这意味着什么:您必须添加一个回调方法或者让您的xml加载类抛出您正在听的某种“完整”事件(表面上或多或少相同的问题)我认为)。


added to answer as mentioned in comment 添加到评论中提到的答案

As mentioned in my comment, you can allow your class to throw custom events by using the EventDispatcher class. 正如我的评论中所提到的,您可以允许您的类通过使用EventDispatcher类来抛出自定义事件。 I am guessing this class was actually used for dispatching events from flash mx components, which is why it is in the mx package. 我猜这个类实际上用于从flash mx组件调度事件,这就是它在mx包中的原因。

For you need to make your own Event class for event objects. 因为您需要为事件对象创建自己的Event类。 From the docs/googling, (can't recall where exactly), at the base level an event has two properties: the target (a reference to the object that threw the event) and a name. 从docs / googling,(无法回想起确切的位置),在基本级别,事件有两个属性:目标(对抛出事件的对象的引用)和名称。 You can always add more later or extend it if you want for your own custom events. 如果您想要自己的自定义事件,您可以随时添加更多或扩展它。

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

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

Was double checking here, And it seems you actually don't need to make your own Event class, it can just be a generic object with these two required properties -but I like to make one anyways to reuse as needed and keep my code cleaner. 在这里进行了双重检查,而且看起来你实际上不需要创建自己的Event类,它只能是具有这两个必需属性的通用对象 - 但我仍然想要根据需要重新使用它并保持我的代码更清洁。

then in your (XML) class: 然后在你的(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

now this adds 3 methods to your class. 现在,这会为您的班级添加3种方法。 Within your class methods you can dispatch an event like so: 在您的类方法中,您可以调度这样的事件:

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

outside of your class you can register an event listener like so: 在课堂之外,您可以注册一个事件监听器,如下所示:

var xmlObject:MyXMLClass = new MyXMLClass();

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

Now, since the event name "complete" is a constant string and should not change, I recommend making it a static constant in the event class like so: 现在,由于事件名称“complete”是一个常量字符串而不应该更改,我建议在事件类中将其设置为静态常量,如下所示:

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;
    }
}

So then you can reference it by going Event.COMPLETE like: 那么你可以通过去Event.COMPLETE来引用它:

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

just to reduce the chance of typos and can refer to the events types you have by looking in your event class. 只是为了减少拼写错误的可能性,并且可以通过查看您的事件类来引用您拥有的事件类型。

Now there is 1 more thing to worry about now: I've found the event handler method runs in the scope of the object that threw the event. 现在还有一件事要担心:我发现事件处理程序方法在抛出事件的对象范围内运行。 You can verify this by adding trace(this); 您可以通过添加trace(this)来验证这一点; to the eventListenerMethod() method, and it should trace out your class, Not where that method was written. 到eventListenerMethod()方法,它应该跟踪你的类,而不是编写该方法的位置。 To solve this, you can use the Delegate class to make that method run in the scope of where it was written. 要解决此问题,可以使用Delegate类使该方法在其编写位置的范围内运行。

so before: 所以之前:

// 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);

you can go: 你可以走了:

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));

Before I was also puzzling over the same issue as you for loading external data and hitting random scope problems and didn't know how to fix them properly (had to come up with 'creative' methods). 之前我还在解决与加载外部数据和遇到随机范围问题相同的问题,并且不知道如何正确修复它们(必须提出“创造性”方法)。 =b = b

Discovering these two things is what allowed me to remain a bit more sane when I had to work in AS2 for a few years and these two things are done a lot better in AS3 (no scope issues/Delegate needed for example, and a lot of objects naturally already extend the EventDispatcher class making it more cleaner to work with or more 'native' as I like to say, and it has an actual Event class you can use/extend so you don't need to make your own). 发现这两件事让我在AS2工作几年后能够保持更加理智,这两件事在AS3中做得更好(例如,没有范围问题/代表需要,以及很多对象自然已经扩展了EventDispatcher类,使得它更干净,或者更像我想说的“本机”,它有一个实际的Event类,你可以使用/扩展,所以你不需要自己创建)。

Please go and try out the latter half of what I wrote to see if you really need to use the Delegate class though as you may not always need it and can sometimes cause its own problem with garbage collection when you need to remove event listeners (in this case my need to keep an explicit reference to the Delegate object you pass in to addEventListener() so you can pass it in to removeEventListener() later. In my experience I've almost always needed it though, which is why I added it to my answer. It can be useful for other things too I've found. By itself you can use it to run methods in the scope of other objects (in this case it is used to run the event listener method in the scope of the _root/"where it was written"). 请去尝试我写的后半部分,看看你是否真的需要使用Delegate类,因为你可能并不总是需要它,当你需要删除事件监听器时,有时会导致垃圾收集本身的问题(在这种情况我需要保持对传入addEventListener()的Delegate对象的显式引用,以便稍后可以将它传递给removeEventListener()。根据我的经验,我几乎总是需要它,这就是为什么我添加它对我的回答。它对我发现的其他东西也很有用。你可以使用它来运行其他对象范围内的方法(在这种情况下,它用于在范围内运行事件监听器方法) _root /“它写的地方”)。

I highly recommend moving to AS3 when possible. 我强烈建议尽可能转移到AS3。 I also recommend looking up the references for EventDispatcher and Delegate classes in the help to get a better idea of what is going on inside -I haven't had to write any real AS2 for a long time and my memory may be a bit fuzzy. 我还建议在帮助中查找EventDispatcher和Delegate类的引用,以便更好地了解内部发生的事情 - 我没有必要长时间写任何真正的AS2,而且我的记忆可能有点模糊。 All of what I wrote just now is mostly from memory. 我刚刚写的所有内容主要来自记忆。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 PHP-如何使函数等待并返回另一个函数值 - PHP - How do I make a function wait and return another functions value 如何使initWithContentsOfURL等待动态生成的xml页面? - How do I make initWithContentsOfURL wait for an xml page that is generated dynamically? 如何从另一个XML行获取相关数据? - How do I get related data from another XML row? 从xml配置文件中从Flash调用JavaScript函数 - Calling a javascript function from a flash from xml config file Flash-如何动态将XML节点名称传递给外部函数? - Flash - How do I dynamically pass XML node name to an external function? Flash中部分加载的XML - Partially loaded XML in Flash API被重定向到xml页面,如何通过调用该api获取该xml页面的内容? - API is redirected to an xml page, how do I get the contents of that xml page by calling that api? 加载了XML文件响应,如何立即从中获取数据? - Loaded an XML file response, how do I get the data out of it now? 如何立即从php表单中获取数据并将数据写入Flash横幅使用的xml文件 - How can I get data from a php form instantly and write datas to a xml file used by a flash banner 如何将XML输入从一种格式转换为另一种格式 - How do I convert the XML input from one format to another
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM