私が 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 を使用することを示唆する回答はあまり役に立ちません。このような大幅な変更を行います。
助けてくれてありがとう!