1

私が Flex で構築しているアプリケーションには、データ プロバイダーとして XML を使用する Flex ツリーがあります。ユーザーは、レイヤーとそれらのレイヤーを保持するフォルダーを作成できる必要があります (Photoshop を考えてください)。また、ユーザーはこれらのアイテムを自由に再配置し、レイヤーをフォルダーにドラッグできる必要があります (必要に応じて、フォルダーを他のフォルダーにドラッグするなど)。

これは、私がやろうとしていることを紹介するために作成したテストアプリケーションです。シンプルにするために、可能な限り基本的なものに分解しました。

(注:内部にダミーノードを含むコードでXMLを作成し、実行時にそのノードを削除しています。これが、起動時にツリーが完全に空白になる唯一の方法です。(空のroot node を指定すると、たとえ showRoot が false であっても、そのルート ノードがツリー内に表示されます)。その効果を達成するためのより良い方法がある場合は、お知らせください。)

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

    <fx:Script>
        <![CDATA[
            import com.custom.model.service.XMLService;

            import mx.events.DragEvent;
            import mx.events.FlexEvent;
            import mx.utils.ObjectUtil;

            protected function treeInit(event:FlexEvent):void
            {
                layersTree.expandChildrenOf(layers_xml, true);

                /*delete the dummy XML node so that the tree is clean on startup 
                and doesn't show the root folder*/
                XMLService.deleteNodeById(layers_xml, 'dummy');
            }

            protected function newFolderClick(event:MouseEvent):void
            {
                // Form New XML Node..
                var xmlObj:Object = XMLService.formFolderXML();

                //...and add it to the XML file.
                if(!layers_xml.children()[0]){
                    //if there are no child nodes, add normally
                    layers_xml.source = xmlObj.xmlNode;
                }else{
                    //if there are child nodes, add them before the previous node
                    layers_xml.prependChild(xmlObj.xmlNode);
                }

                /*immediately expand the folders to allow them to be dragged into,
                closed folders cannot be dragged into*/
                layersTree.expandChildrenOf(layers_xml, true);
            }

            protected function newTextLayerClick(event:MouseEvent):void
            {
                // Form New XML Node..
                var xmlObj:Object = XMLService.formTextLayerXML();

                //...and add it to the XML file.
                if(!layers_xml.children()[0]){
                    //if there are no child nodes, add normally
                    layers_xml.source = xmlObj.xmlNode;
                }else{
                    //if there are child nodes, add them before the previous node
                    layers_xml.prependChild(xmlObj.xmlNode);
                }

                //programatically select THIS item so it highlights in the tree
                layersTree.selectedIndex = (layersTree.showRoot) ? 1 : 0;
            }

            protected function onDragDrop(event:DragEvent):void
            {
                trace('action:', event.action);
                trace('dragDrop', ObjectUtil.toString(layers_xml));
            }

            protected function onDragComplete(event:DragEvent):void
            {
                trace('action:', event.action);
                trace('dragComplete', ObjectUtil.toString(layers_xml));
            }

        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->

        <fx:XML id="layers_xml">
            <layers label="root" id="root">
                <layer label="DummyLayer2" type="layer" id="dummy" isVisible="false" />
            </layers>
        </fx:XML>

    </fx:Declarations>

    <s:VGroup gap="0" width="100%">

        <mx:Tree id="layersTree" width="100%" minHeight="41" allowMultipleSelection="false"
                 borderVisible="false" creationComplete="treeInit(event)"
                 dataProvider="{layers_xml}" dragEnabled="true" dropEnabled="true" 
                 focusColor="#FFFFFF" labelField="@label"
                 rollOverColor="#D1EEEE" selectionColor="#D1EEEE" showRoot="false" 
                 dragDrop="onDragDrop(event)" dragComplete="onDragComplete(event)"/>

        <s:Group width="100%">
            <s:HGroup id="layerButtons" horizontalCenter="0">
                <s:Button id="newFolderBtn" label="Folder" buttonMode="true"
                         click="newFolderClick(event)"/>
                <s:Button id="newTextFieldBtn" label="TextLayer" buttonMode="true"
                    click="newTextLayerClick(event)" />
            </s:HGroup>
        </s:Group>

    </s:VGroup>

</s:Application>

以下は、ツリーに挿入する XML ノードを生成するために使用しているクラス (XMLService) です。

package com.custom.model.service
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;

public class XMLService extends EventDispatcher
{

    private static var _folderLayerCount:int = 0;
    private static var _textLayerCount:int = 0;
    private static var _layerName:String;
    private static var _layerId:String;


    public function XMLService(target:IEventDispatcher=null)
    {
        super(target);
    }


    private static function randomId(length:int = 9):String{
        //function to generate randomId for XML
        var chars:String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        var num_chars:Number = chars.length - 1;
        var randomChar:String = "";

        for (var i:Number = 0; i < length; i++){
            randomChar += chars.charAt(Math.floor(Math.random() * num_chars));
        }
        return randomChar;
    }


    public static function formTextLayerXML(name:String = null):Object {
        //this static function forms the xml node for text layers.
        _textLayerCount++;
        _layerId = randomId();
        _layerName = (name != null) ? name : ('Text Layer ' + _textLayerCount);

        var textNode:XML = new XML('<layer label="' + _layerName + '" type="textlayer" id="'+ _layerId +'" isVisible="true" />');

        //returns the node and the unique id as a string    
        return {xmlNode : textNode, id : _layerId};
    }

    public static function formFolderXML():Object {
        //this static function forms the xml node for folders.
        _folderLayerCount++;
        _layerId = randomId();
        _layerName = 'Folder ' + _folderLayerCount;

        var folderNode:XML = new XML('<folder label="' + _layerName + '" type="folder" id="'+ _layerId +'" isBranch="true" isVisible="true" />');
        //isBranch = true makes the node behave as a folder, even if it's empty

        //returns the node and the unique id as a string
        return {xmlNode: folderNode, id: _layerId}; 
    }

    public static function deleteNodeById(xml:XML, idToDelete:String):void {

        var count:int = 0; /*have to keep track of our own count
        because targeting will not work for xml deletes */
        var descendNodes:XMLList = xml.descendants();

        for each (var layer:XML in descendNodes) {
            if(descendNodes[count].@id == idToDelete) {
                delete descendNodes[count];
            }
            count++;
        }
    }

}
}

私が抱えている問題は、フォルダーとレイヤーが作成され、レイヤーがフォルダーにドラッグされると、そのフォルダーがフォルダーからドラッグされると、レイヤーが移動される代わりにレイヤーのコピーが作成されることです。

動的に作成されるフォルダーとレイヤー:

動的に作成されるフォルダーとレイヤー

フォルダー内でレイヤーをドラッグ:

フォルダー内のレイヤーをドラッグ

レイヤーをフォルダーの外 (および上) にドラッグするとどうなりますか:

レイヤーをフォルダーの外 (および上) にドラッグするとどうなるか

これは、dragComplete でlayers_xml をトレースしているときに XML で発生することがわかります。

<layers label="root" id="root">
  <layer label="Text Layer 1" type="textlayer" id="GMccFXr5m" isVisible="true"/>
  <folder label="Folder 1" type="folder" id="JfoN1yo1I" isBranch="true" isVisible="true">
    <layer label="Text Layer 1" type="textlayer" id="GMccFXr5m" isVisible="true"/>
  </folder>
</layers>

「フォルダー 1」にあったテキスト レイヤー (「テキスト レイヤー 1」) は、フォルダー ノードの外側に移動されるのではなく、フォルダー ノードの外側にコピーされました。

ここが奇妙な部分です。

Tree Flex コンポーネントで showRoot が true に設定されている場合、この問題はなくなります。問題は、ツリーに表示されるルート ノードが望ましくないことです。

Tree コンポーネントに次の小さな変更を加えます。

showRoot="true"

showRoot が true のときにまったく同じ手順を実行した場合、トレースされているlayers_xml を見てみましょう。

<layers label="root" id="root">
  <layer label="Text Layer 1" type="textlayer" id="nwdwAwlML" isVisible="true"/>
  <folder label="Folder 1" type="folder" id="TdVbaPvPB" isBranch="true" isVisible="true"/>
</layers>

Flex でバグに遭遇しましたか? または、コード内の何かがこれを引き起こしていますか?

なぜこれが起こっているのかについての回答や説明は大歓迎ですが、XML 以外の任意のタイプの dataProvider を使用することを示唆する回答はあまり役に立ちません。このような大幅な変更を行います。

助けてくれてありがとう!

4

1 に答える 1

3

さらに調査した結果、問題は解決されました。

この問題の原因は実際には Flex のバグです。ツリー コントロールに項目を動的に追加すると Flex が不安定になるためです (自由に再配置できる固定データ セットを使用するのとは対照的です)。

openItemsこれを回避するには、現在のとのスナップショットをキャプチャし、scrollPosition手動で を再dataProvider接続して全体を再検証することで、Flex がツリーの現在の構造について混乱しないようにします。

dragCompleteその関数を作成し、イベントで関数を呼び出しました。

protected function onDragComplete(event:DragEvent):void {
     //resetting the entire tree after every drag completes
    forceTreeRedraw(layersTree, layers_xml);
}

private function forceTreeRedraw(tree:Tree, dataProvider:Object):void {
    var scrollPosition:Number = tree.verticalScrollPosition;
    var openItems:Object = tree.openItems;
    tree.dataProvider = dataProvider;
    tree.openItems = openItems;
    tree.validateNow();
    tree.verticalScrollPosition = scrollPosition;
}

問題は魔法のように解決され、ふわふわのバニーははしゃぎ、すべてがうまくいっています。

すべての功績は、ここに投稿された回答にあります: Flex: Updating a Tree control

于 2012-12-02T15:40:55.197 に答える