0

XML ファイルにアクセスし、そのコンテンツを取得し、その情報を使用して多数のテキスト フィールドに入力する必要があるアプリケーションを Actionscript 2 で作成しています。これは、フレームベースのアクション スクリプトでは簡単でした。なぜなら、私がしなければならなかったことは、何かをやめて、次のステップを XML.onLoad 関数から実行するように設定するだけだったからです。しかし、私はこのことをクラスファイルで書き直しています.XMLが既に存在することに依存している関数が呼び出される前に、XMLをロードするのに苦労しています.

詳しく説明すると、

クラス、メインがあるとしましょう。そのクラスは次のようになります。

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

}

これで、getXML 関数を呼び出せるように作成する XMLLoader オブジェクトができました (心配しないでください。最後の関数です)。

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

うーん、誰かがこれについて私を修正するかもしれませんが、フラッシュが外部リソース(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 クラスを使用して、クラスがカスタム イベントをスローできるようにすることができます。このクラスは実際にはフラッシュ mx コンポーネントからイベントをディスパッチするために使用されていたと推測されます。これが mx パッケージに含まれている理由です。

イベント オブジェクト用に独自の Event クラスを作成する必要があるためです。ドキュメント/グーグルから(正確にどこを思い出すことはできません)、ベースレベルでは、イベントにはターゲット(イベントをスローしたオブジェクトへの参照)と名前の2つのプロパティがあります。独自のカスタム イベントが必要な場合は、後でいつでも追加したり、拡張したりできます。

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

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

ここで再確認しましたが、実際には独自の Event クラスを作成する必要はないようです。これらの 2 つの必須プロパティを持つ汎用オブジェクトにすることができますが、必要に応じて再利用し、コードをきれいに保つために、いずれかを作成するのが好きです.

次に、(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);

ここで、イベント名「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;
    }
}

したがって、次のように Event.COMPLETE に移動することで参照できます。

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

タイプミスの可能性を減らすためだけに、イベント クラスを調べることで、持っているイベント タイプを参照できます。

もう 1 つ気になる点があります。イベント ハンドラー メソッドが、イベントをスローしたオブジェクトのスコープ内で実行されていることがわかりました。これを確認するには、trace(this); を追加します。そのメソッドが書かれた場所ではなく、クラスをトレースする必要があります。これを解決するには、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

これら 2 つのことを発見したことで、私が数年間 AS2 で作業しなければならなかったときに、もう少し正気を保つことができました。これら 2 つのことは、AS3 でははるかにうまく行われています (たとえば、スコープの問題はありません/デリゲートは必要ありません。オブジェクトは当然、既に EventDispatcher クラスを拡張しており、私が言いたいように、操作がよりクリーンまたはより「ネイティブ」になり、使用/拡張できる実際の Event クラスがあるため、独自に作成する必要はありません)。

私が書いたことの後半を試して、Delegate クラスを本当に使用する必要があるかどうかを確認してください。ただし、常に必要であるとは限らず、イベント リスナーを削除する必要がある場合にガベージ コレクションで問題が発生することがありますこの場合、後で removeEventListener() に渡すことができるように、addEventListener() に渡すデリゲート オブジェクトへの明示的な参照を保持する必要があります。私の答えに. それは私が見つけた他のものにも役立ちます. それ自体で、他のオブジェクトのスコープでメソッドを実行するために使用できます (この場合、オブジェクトのスコープでイベントリスナーメソッドを実行するために使用されます) _root/"それが書かれた場所")。

可能であれば AS3 に移行することを強くお勧めします。また、ヘルプで EventDispatcher および Delegate クラスの参照を調べて、内部で何が起こっているかをよりよく理解することをお勧めします - 私は長い間本物の AS2 を書く必要がなかったので、私の記憶は少し曖昧かもしれません. いま書いたことはほとんど記憶からです。:)

于 2013-01-27T11:06:06.270 に答える