判りやすく言うと、explorer のアノ画面(←)です。
これを使ってアプリケーションを作れば、いかにもソレっぽく見えてカッコイイ訳ですが・・・
製作者側としては、データの構築方法&管理方法に注意が必要となります。
基本的記述
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable] private var treeXml:XML=<root label="root">
<dir label="dir0"></dir>
<dir label="dir1">
<dir label="child0"></dir>
<dir label="child1">
<dir label="data0"></dir>
<dir label="data1"></dir>
<data/>
</dir>
</dir>
</root>;
]]>
</mx:Script>
<mx:Tree width="100%" height="100%"
labelField="@label"
dataProvider="{treeXml}"></mx:Tree>
</mx:Application>
データとして使う XML は [Bindable] が無いと、<mx:Tree> の dataProvider に設定出来ません。コンパイルで警告が出てしまいます。
<root>以下のXMLの記述が何故かラベルテキストに。
ルートが一つ
しかし、このままではルートに一つしかアイテムを設定する事が出来ません。副作用として、うっかり <root> の label 属性を忘れても大丈夫になりました。
そもそも XML の仕様として、ルートは一つと決まっています。
発想の転換?と言うほどではありませんが、管理するデータを XML ではなく XMLList にできないでしょうか?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable] private var treeXml:XMLList = new XMLList(
'<dir label="dir0"></dir>'
+ '<dir label="dir1">'
+ '<dir label="child0"></dir>'
+ '<dir label="child1">'
+ '<dir label="data0"></dir>'
+ '<dir label="data1"></dir>'
+ '<data/>'
+ '</dir>'
+ '</dir>');
]]>
</mx:Script>
<mx:Tree width="100%" height="100%"
labelField="@label"
dataProvider="{treeXml}"></mx:Tree>
</mx:Application>
mxml ならば、Action Script のコードとしてではなく、直接 mxml でデータを書くことが出来ます。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:XMLList id="treeXml">
<dir label="dir0"></dir>
<dir label="dir1">
<dir label="child0"></dir>
<dir label="child1">
<dir label="data0"></dir>
<dir label="data1"></dir>
<data/>
</dir>
</dir>
</mx:XMLList>
<mx:Tree width="100%" height="100%"
labelField="@label"
dataProvider="{treeXml}"></mx:Tree>
</mx:Application>
<mx:XMLList> を更に <mx:XMLListCollection> で括って、id属性の指定を<mx:XMLListCollection> に移し、データを <mx:Tree> の中に置けば・・・
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Tree width="100%" height="100%"
labelField="@label">
<mx:XMLListCollection id="treeXml">
<mx:XMLList>
<dir label="dir0"></dir>
<dir label="dir1">
<dir label="child0"></dir>
<dir label="child1">
<dir label="data0"></dir>
<dir label="data1"></dir>
<data/>
</dir>
</dir>
</mx:XMLList>
</mx:XMLListCollection>
</mx:Tree>
</mx:Application>
実は、XML、XMLList は dataProvider に設定される際に、自動的に XMLListCollection にラップされるようです。
ノードデータの変更
操作によって、ツリービューのノードデータを動的に操作したい場合はどうしたら良いでしょうか?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function nodeClick():void
{
var xml:XML = treeView.selectedItem as XML;
if ( xml ) xml.appendChild(<text label="added"/>);
}
]]>
</mx:Script>
<mx:Tree id="treeView" width="100%" height="100%"
itemClick="nodeClick();"
labelField="@label">
<mx:XMLListCollection id="treeXml">
<mx:XMLList>
<dir label="dir0"></dir>
<dir label="dir1">
<dir label="child0"></dir>
<dir label="child1">
<dir label="data0"></dir>
<dir label="data1"></dir>
<data/>
</dir>
</dir>
</mx:XMLList>
</mx:XMLListCollection>
</mx:Tree>
</mx:Application>
label 属性のない <data/> をクリックすると・・・冒頭で述べた「うっかり label 指定をわすれた<root>」と同じ現象になり、XML 記述が表示されてしまいます。
<mx:Tree> の itemClick はノードがクリックされた時のアクションを指定できます。
ノードがクリックされるという事は、ノードの選択作業になるので、呼ばれた関数 nodeClick() 内では、選択されているノードを treeView.selectedItem で取得しています。有効な XML ならば、そこに appendChild するというコードです。
mxml でハードコーディングされた XMLList のデータは、いわゆる「constデータ」ではなく、変更&編集が可能です。
XML以外のデータをdataProviderに指定したい
XML は階層構造を持つデータの管理に対して大変強力です。しかし、データ数が多くなると、場合にも寄りますが大変重くなります。XML 以外の選択肢は無いのでしょうか。
[Bindable] private var treeData:Object =
{
label:"root",
children:
[
{ label:"dir0" },
{ label:"dir1" }
]
};
左記のような Action Script を記述し、treeData を dataProvider に指定する事が出来ます。 この場合、<mx:Tree> には labelField 指定は必要ないようです。
ノードとなる Object の lavel プロパティが表示テキストに、children プロパティにある Array が子ノード Object の配列と認識されるようです。
[Bindable] private var treeData:Array =
[
{ label:"dir0" },
{ label:"dir1",
children:
[
{ label:"child0" },
{ label:"child1" }
]
}
];
また、Object 配列を直接指定することも出来ます。
この際の、Object と Array の扱いは、前述と同じです。
このような記述をすると・・・
[Bindable] private var treeData:Array =
[
{ label:"dir0",children:null },
{ label:"dir1",children:[] }
];
children プロパティが存在するだけでは親ノードになることは出来ず、逆に長さ 0 であっても Array が指定されていれば、子ノードを持つ親と判定されるようです。
Object&ArrayのdataProviderでの、ノードデータの変更
実行してみると・・・
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable] private var treeData:Array =
[
{ label:"dir0" },
{ label:"dir1" }
];
//
private function nodeClick():void
{
var obj:Object = treeView.selectedItem as Object;
if ( obj )
{
var childs:Array = obj.children as Array;
if ( !childs ) childs = obj.children = new Array();
childs.push( { label:"added" } );
}
}
]]>
</mx:Script>
<mx:Tree id="treeView" width="100%" height="100%"
itemClick="nodeClick();"
dataProvider="{treeData}">
</mx:Tree>
</mx:Application>
クリックしても画面が変化しません。ノードを開くなりの操作をすると、やっとノードデータの変更が画面に反映されるようです。
実は XMLListCollection は、ノードの変更を<mx:Tree> に伝える仕組みを持っています。その為、XMLでデータを形成していた時は、変更が即座に画面へ反映されたのです。
Object&Array でデータを作る場合、データ変更を <mx:Tree> ;に明示的に知らせる必要があります。
→参考:http://www.adobe.com/livedocs/flex/3_jp/langref/mx/controls/treeClasses/DefaultDataDescriptor.html
private function nodeClick():void
{
var obj:Object = treeView.selectedItem as Object;
if ( obj )
{
treeView.dataDescriptor.addChildAt(obj, { label:"added" }, 0x7fffffff);
}
}
dataProvider は、ITreeDataDescriptor をインターフェースとして持っているようですが、これの元を辿ると、ICollectionView へ至ります。これは、IEventDispatcher を継承しているようです。
また、XMLListCollection は、ICollectionView を実装しているという関係から、ICollectionView を調べて見ました。
→参照:http://www.adobe.com/livedocs/flex/3_jp/langref/mx/collections/ICollectionView.html
ICollectionView は、データの更新があると mx.events.CollectionEvent.COLLECTION_CHANGE というイベントを発行します。
→参照:http://livedocs.adobe.com/flex/3_jp/langref/mx/events/CollectionEvent.html
かなり無理やりですが、更新イベントを発行するコードを追加してみました。
private function nodeClick():void
{
var obj:Object = treeView.selectedItem as Object;
if ( obj )
{
treeView.dataDescriptor.addChildAt(obj, { label:"added" }, 0x7fffffff);
treeView.dataProvider.dispatchEvent(
new CollectionEvent(
CollectionEvent.COLLECTION_CHANGE,
false,
false,
CollectionEventKind.ADD,
-1,
-1,
[]
)
);
}
}
ただ、CollectionEvent に関しては情報が少なく、ドキュメントを読んでもパラメータの内訳の理解が正直微妙です。
もしかしたら、問題が起きる可能性もあります。
結論
よほどの事がない限り、データは XML で作り XMLListCollection を dataProvider に登録しよう!dataProvider 関連の記事を検索すると、XML でのデータ構築方法ばかりがヒットします。 実際は、Object&Array でも階層データを構築出来るのに、何故この情報が少ないのか疑問でした。
で、頑張ってみたわけですが、調べて納得(笑)。
一部理解が微妙な部分があるのもありますが(ぉ)、面倒が嫌なら XML でササッとコーディングした方が、手間も少ないようです。

コメントする