Flex 3快速入门(19): 构建高级用户界面 使用 Tree 控件
?
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" viewSourceURL="src/index.html">
?提示:可以通过右键flash查看官方源文件
在运行时添加一个空的枝节点
可以在运行时向Tree控件添加空的枝节点。下边的例子展示了通过数据提供器(data provider)API和数据描述器(data descriptor )API添加枝节点。通常,添加枝节点的首选途径是通过数据提供器(data provider)API。
addEmptyBranthDP()方法通过数据提供器API向使用XML数据提供器的Tree组件增加一个空节点,这个方法为新节点创建一个XML类型的变量,并且设置这个节点的isBranch属性为true来创建一个枝节点。然后这个方法调用Tree控件的dataProvider.addItemAt()方法来向Tree控件的数据提供器增加新的元素。
addEmptyBranchDP2()方法通过数据提供器API向使用对象数据提供器的Tree组件增加一个空节点,这个方法使用children属性创建一个新的对象,使用children属性能够确保isBranch()方法返回true。然后,这个方法调用Tree控件的dataProvider.addItemAt()方法来向Tree控件的数据提供器增加新的对象。
addEmptyBranchDD()方法通过数据描述器(data descripter)API。这个方法创建一个XML类型的变量,并且设置isBranch属性为true来创建一个枝节点。然后,这个方法调用Tree控件的dataDescriptor.addChildAt()方法来向Tree控件的数据描述器添加一个新的子节点。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" viewSourceURL="src/index.html">??? <mx:Script>
??????? <![CDATA[
??????????? [Bindable]
??????????? private var dataX:XML =
??????????????? <item label="Top">
??????????????? <item label="Child One"/>
??????????????? <item label="Child Two" />
??????????? </item>;
??????
??????????? [Bindable]
??????????? private var dataObj:Object =
??????????????? [{label:"Top", children:
??????????????? [
??????????????? {label:"Child One"}, {label: "Child Two"}
??????????????? ]
??????????????? }];
??????
??????????? // Adding a branch by going through the Tree control's dataProvider. This is
??????????? // the preferred method.
??????????? // Toggling the isBranch attribute to true causes DefaultDataDescriptor.isBranch()
??????????? // to return true and the Tree treats the node as a branch.
??????????? private function addEmptyBranchDP():void
??????????? {
??????????????? var newNode:XML = <item label='Middle' isBranch="true"></item>;
??????????????? xmlBound2Tree.dataProvider.addItemAt(newNode, 1);
??????????? }
??????
??????????? // For an object graph, the key point is that the children property needs to be defined,
??????????? // even if it is empty.
??????????? // This causes isBranch() to return true and the Tree treats the new node as a branch.
??????????? private function addEmptyBranchDP2():void
??????????? {
??????????????? var newObj:Object = {label:"Middle", children:[]};
??????????????? objGraphBound2Tree.dataProvider.addItemAt(newObj, 1);
??????????? }
??????
??????????? // Adding a branch by going through the Tree control's dataDescriptor.
??????????? private function addEmptyBranchDD():void
??????????? {
??????????????? var newNode:XML = <item label='Child 4' isBranch="true"></item>;
??????????????? xmlBound2Tree.dataDescriptor.addChildAt(dataX, newNode, 2, dataX);
??????????? }
????????? ]]>
??? </mx:Script>
??? <mx:Label text="Tree with XML data"/>
??? <mx:Tree id="xmlBound2Tree" dataProvider="{dataX}" labelField="@label" showRoot="true" width="200"/>
??? <mx:Button label="Add Empty Branch through the dataProvider" click="addEmptyBranchDP();"/>
??? <mx:Button label="Add Empty Branch through the dataDescriptor" click="addEmptyBranchDD();"/>
??? <mx:Spacer height="10"/>
??? <mx:Label text="Tree with object data"/>
??? <mx:Tree id="objGraphBound2Tree" dataProvider="{dataObj}" width="200"/>
??? <mx:Button label="Add Empty Branch through the dataProvider" click="addEmptyBranchDP2();"/>??
</mx:Application>
结果
打开树到指定的节点
默认的,Tree控件在初始化后是收缩的,你也许不确定如何初始化控件时展开数,并且选定指定的节点。下边的例子,展示了如何实现它。在这个程序中,initTree()方法,在Tree控件被创建后调用。这个方法展开Tree控件的根节点,并且设置selectedIndex属性为指定节点的索引号。
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" viewSourceURL="src/index.html">??? <mx:Script>
??????? <![CDATA[
??????????? import flash.events.*;
??????????? import mx.events.*;
??????????? import mx.controls.*;
??????????? private function initTree():void {
??????????????? XMLTree1.expandItem(MailBox.getItemAt(0), true);
??????????????? XMLTree1.selectedIndex = 2;
??????????? }
??????? ]]>
??? </mx:Script>
??? <mx:Tree id="XMLTree1" width="150" height="170"
???????????? labelField="@label" creationComplete="initTree();">
??????? <mx:XMLListCollection id="MailBox">
??????????? <mx:XMLList>
??????????????? <node label="Mail" data="100">
??????????????????? <node label="Inbox" data="70"/>
??????????????????? <node label="Personal Folder" data="10">
??????????????????????? <node label="Business" data="2"/>
??????????????????????? <node label="Demo" data="3"/>
??????????????????????? <node label="Saved Mail" data="5" />
??????????????????? </node>
??????????????????? <node label="Sent" data="15"/>
??????????????????? <node label="Trash" data="5"/>
???????????????? </node>
??????????? </mx:XMLList>
??????? </mx:XMLListCollection>
</mx:Tree>
</mx:Application>
结果
读取多节点名XML文档
你可以从具有多个节点名字的XML文档中组装Tree 控件。下边的例子实现了这个功能。Tree控件的数据提供者是一个XMLListCollection,它组装自一个包含folder和Pfolder元素的XMLList。Tree控件的labelField属性被设置为label属性,不管元素叫什么名字,这个属性对XMLList中的所有属性都是公共的。
例子
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" viewSourceURL="src/index.html">
??? <mx:Tree id="tree1" dataProvider="{MailBox}" labelField="@label" showRoot="true" width="160"/>
??? <mx:XMLListCollection id="MailBox" source="{Folders}"/>
???? <mx:XMLList id="Folders">
??????? <folder label="Mail">
??????????? <folder label="INBOX"/>
??????????? <folder label="Personal Folder">
??????????????? <Pfolder label="Business" />
??????????????? <Pfolder label="Demo" />
??????????????? <Pfolder label="Saved Mail" />
??????????? </folder>??????????? <folder label="Sent" />
??????????? <folder label="Trash" />
??????? </folder>
??? </mx:XMLList>???
</mx:Application>
结果
当数据提供器更新时保持Tree控件打开
默认地,当数据提供器更新数据时Tree控件收缩。下边的例子展示了当数据提供器更新时保持Tree控件打开的方法。
在这个应用程序中,当一个用户单击Button控件时changeProvider()方法更新数据提供器。通常,这会导致Tree控件收缩。然而,Tree控件的渲染事件处理器,renderTree()方法,放置收缩的发生。当changerProvider()方法被调用,当前状态是打开的元素被保存到对象open变量中。当数据被刷新时,被命名为refreshData的Boolean类型的变量被置为true。renderTree()方法调用Tree控件的invalidateList()方法来刷新树的行。然后置refreshDate属性为false,并且重置Tree控件的打开元素属性为open对象变量,这个变量在刷新前就包含了状态是打开的元素。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" initialize="initTree()" viewSourceURL="srcview/index.html">??? <mx:Script>
??????? <![CDATA[
??????????? [Bindable]
??????????? public var open:Object = new Object();
??????????? [Bindable]??????????? public var refreshData:Boolean = false;
??????????? [Bindable]
??????????? public var switchObj:Object = new Object();
??????????? [Bindable]??????????? public var firstObj:Object = new Object();
??????????? [Bindable]??????????? public var firstObj1:Object = new Object();
??????????? [Bindable]??????????? public var firstObj2:Object = new Object();
??????????? [Bindable]??????????? public var provider:String = "firstObj";
??????????? private function initTree():void
??????????? {
??????????????? firstObj = new Object();
??????????????? firstObj.label = "Foods";
??????????????? firstObj.children = new Array();
??????????????? firstObj1.label = "Fruits";
??????????????? firstObj1.children = new Array();
??????????????? firstObj2.label = "Oranges";
??????????????? firstObj1.children[0] = firstObj2;
??????????????? firstObj.children[0] = firstObj1;
??????????????? switchObj = firstObj;
??????????? }??????????? public function changeProvider():void
??????????? {
??????????????? open = SampleTree.openItems;
??????????????? refreshData = true;
??????????????? if (provider == "firstObj")??????????????? {
??????????????????? provider = "switchObj";
??????????????????? SampleTree.dataProvider = switchObj;
??????????????? }
??????????????? else??????????????? {
??????????????????? provider = "firstObj";
??????????????????? SampleTree.dataProvider = firstObj;
??????????????? }
??????????? }??????????? public function renderTree():void{
??????????????? if(refreshData){??????????????????? // Refresh all rows on next update.
??????????????????? SampleTree.invalidateList();
??????????????????? refreshData = false;
??????????????????? SampleTree.openItems = open;
??????????????????? // Validate and update the properties and layout
??????????????????? // of this object and redraw it, if necessary.
??????????????????? SampleTree.validateNow();
??????????????? }??????????? }
??????? ]]>
??? </mx:Script>
??? <mx:Tree id="SampleTree" render="renderTree()" width="250" dataProvider="{firstObj}" labelField="label" />??? <mx:Button label="Change Data Provider" click="changeProvider()"/>
</mx:Application>
结果
项和一个树控件拖放
?
拖拽元素到Tree控件,和从Tree控件拖拽出元素创建一个应用程序,实现拖拽元素从Tree控件中,或到Tree控件是令人畏惧的,很明显,因为需要大量的事件处理逻辑。本节提供2个例子,来示范实现这2种功能的技术。从Tree控件中拖拽出元素下边的例子展示如何从Tree控件中拖拽元素到DataGrid控件中。Tree控件的数据提供器是XML对象。通过拖拽事件处理方法前的注释可以使你读懂整个应用程序。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">??? <mx:Script>
??????? <![CDATA[
??????????? import mx.controls.Alert;
??????????? import mx.controls.Label;
??????????? import mx.controls.List;
??????????? import mx.collections.ArrayCollection;
??????????? import mx.core.DragSource;
??????????? import mx.controls.Tree;
??????????? import mx.controls.DataGrid;
??????????? import mx.controls.listClasses.ListBase;
??????????? import mx.events.DragEvent;
??????????? import mx.containers.Canvas;
??????????? import mx.managers.DragManager;
??????????? import mx.core.UIComponent;
???????
??????????? [Bindable]??????????? private var dataGridProvider:ArrayCollection = new ArrayCollection();
??????????? /**
???????????? * Handles the dragEnter event on the DataGrid control.
???????????? * If the dragInitiator is the Tree control, then only nodes of type "restaurant"
???????????? * are permitted to be dropped.
???????????? * Here you can see that by examining the dragSource you can determine if
???????????? * the control should accept the drop. The DataGrid control would not
???????????? * know how to treat a branch+children from the Tree control, so only leaf (restaurant)
???????????? * nodes are accepted.
???????????? */??????????? private function onDragEnter( event:DragEvent ) : void
??????????? {???????????
??????????????? if( event.dragInitiator is Tree ) {
??????????????????? var ds:DragSource = event.dragSource;
??????????????????? if( !ds.hasFormat("treeItems") ) return;???? // no useful data??????????????????? var items:Array = ds.dataForFormat("treeItems") as Array;
??????????????????? for(var i:Number=0; i < items.length; i++) {??????????????????????? var item:XML = XML(items[i]);
??????????????????????? if( item.@type != "restaurant" ) return; // not what we want??????????????????? }
??????????????? }
????????????? // If the Tree control passes or the dragInitiator is not a Tree control,
???????????? // accept the drop.
??????????? DragManager.acceptDragDrop(UIComponent(event.currentTarget));
??????????? }???????
??????????? /**
???????????? * Handles the dragOver event on the DataGrid control.
???????????? * If the dragInitiator is the Tree control, only copy is allowed. Otherwise, a move
???????????? * or link can take place from the List control.
???????????? */??????????? private function onDragOver( event:DragEvent ) : void
??????????? {
??????????????? if( event.dragInitiator is Tree ) {
??????????????????? DragManager.showFeedback(DragManager.COPY);
??????????????? } else {??????????????????? if (event.ctrlKey)
??????????????????? DragManager.showFeedback(DragManager.COPY);
??????????????????? else if (event.shiftKey)??????????????????????? DragManager.showFeedback(DragManager.LINK);
??????????????????? else {
??????????????????????? DragManager.showFeedback(DragManager.MOVE);
??????????????????? }??????????????? }
??????????? }???????
??????????? /**
???????????? * Handles the dragExit event on the drop target and just hides the
???????????? * the drop feedback.
???????????? */
??????????? private function onDragExit( event:DragEvent ) : void??????????? {
??????????????? var dropTarget:ListBase=ListBase(event.currentTarget);??
??????????? dropTarget.hideDropFeedback(event);
??????????? }??????????? /**
???????????? * Handles the dragDrop event on the DataGrid when the
???????????? * drag proxy is released.
???????????? */
??????????? private function onGridDragDrop( event:DragEvent ) : void??????????? {
??????????????? var ds:DragSource = event.dragSource;
??????????????? var dropTarget:DataGrid = DataGrid(event.currentTarget);
??????????????? var arr:Array;
??????????????? if( ds.hasFormat("items") ) {??????????????????? arr = ds.dataForFormat("items") as Array;
??????????????? } else if( ds.hasFormat("treeItems") ) {??????????????????? arr = ds.dataForFormat("treeItems") as Array;
??????????????? }
??????????????? for(var i:Number=0; i < arr.length; i++) {??????????????????? var node:XML = XML(arr[i]);
??????????????????? var item:Object = new Object();
??????????????????? item.label = node.@label;
??????????????????? item.type? = node.@type;
??????????????????? dataGridProvider.addItem(item);
??????????????? }??????????????? onDragExit(event);
??????????? }
??????????? /**
??????????? * Intercepts the dragComplete event on the Tree control
??????????? * and prevents the default behavior from happening. This is necessary
??????????? * if the item being dragged from the Tree control is dropped on a non-Tree
??????????? * object, such as the DataGrid.
??????????? */
??????????? private function onTreeDragComplete(event:DragEvent):void {??????????????? event.preventDefault();
??????????? }???????
??????????? /**
??????????? * Selects all of the items in the List if Ctrl+A is picked when the List control
??????????? * has focus.
??????????? */
??????????? private function selectAllMaybe( event:KeyboardEvent ) : void??????????? {
??????????????? if( event.ctrlKey && event.keyCode == 65 ) {??????????????????? var l:List = List(event.currentTarget);
??????????????????? var allItems:Array = new Array(l.dataProvider.length);
??????????????????? for(var i:Number=0; i < allItems.length; i++) {??????????????????????? allItems[i] = i;
??????????????????? }
??????????????????? l.selectedIndices = allItems;
??????????????? }??????????? }
??????? ]]>
??? </mx:Script>
??? <mx:XML id="treeData" xmlns="">
??????? <root>??????????? <node label="Massachusetts" type="state" data="MA">
??????????????? <node label="Boston" type="city" >
??????????????????? <node label="Smoke House Grill" type="restaurant" />
??????????????????? <node label="Equator" type="restaurant" />
??????????????????? <node label="Aquataine" type="restaurant" />
??????????????????? <node label="Grill 23" type="restaurant" />??????????????? </node>
??????????????? <node label="Provincetown" type="city" >
??????????????????? <node label="Lobster Pot" type="restaurant" />
??????????????????? <node label="The Mews" type="restaurant" />
??????????????? </node>
??????????? </node>??????????? <node label="California" type="state" data="CA">
??????????????? <node label="San Francisco" type="city" >
??????????????????? <node label="Frog Lane" type="restaurant" />
??????????????? </node>
??????????? </node>
??????? </root>??? </mx:XML>
???????
??? <mx:Label x="34" y="40" text="Drag items from this Tree"/>
??? <mx:Label x="34" y="55" text="(items are copied)"/>??? <mx:Tree x="34" y="81" width="181" height="189"
??????? dataProvider="{treeData.node}"
??????? labelField="@label"
??????? dropEnabled="false"
??????? dragEnabled="true"
??????? dragComplete="onTreeDragComplete(event)"
??????? dragMoveEnabled="false"
??????? />??? <mx:Label x="291" y="55" text="Drop items from Tree here"/>
??? <mx:DataGrid x="291" y="81" height="189"
??????? dragEnabled="true"
??????? dataProvider="{dataGridProvider}"
??????? dragEnter="onDragEnter(event)"
??????? dragOver="onDragOver(event)"
??????? dragDrop="onGridDragDrop(event)"
??????? dragExit="onDragExit(event)">??????? <mx:columns>
??????????? <mx:DataGridColumn headerText="Label" dataField="label"/>
??????????? <mx:DataGridColumn headerText="Type" dataField="type"/>??????? </mx:columns>
??? </mx:DataGrid>
</mx:Application>
树控件拖放
下面的示例演示如何将项从列表控件拖到树控件。 树数据提供程序是XML对象。
例子
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
??? <mx:Script>
??????? <![CDATA[??????????? import mx.events.DragEvent;
??????????? import mx.managers.DragManager;
??????????? import mx.core.DragSource;
??????????? import mx.core.UIComponent;
??????????? import mx.controls.Tree;
??????????? /**
???????????? * Called as soon as the dragProxy enters the target. You can add logic
???????????? * to determine if the target will accept the drop based on the
???????????? * dragInitiator, the data available in the dragSource.
???????????? * Here the drop is blindly accepted.
???????????? */??????????? private function onDragEnter( event:DragEvent ) : void
??????????? {
??????????????? DragManager.acceptDragDrop(UIComponent(event.currentTarget));
??????????? }
??????????? /**
???????????? * Called while the dragProxy is over the drop target. You can
???????????? * use this function to determine the type of feedback to show.
???????????? * Since the List is set to allow MOVE (the item is deleted
???????????? * once dropped), different feedback possibilities are given.
???????????? *
???????????? * Also, for this application, the Tree control node the dragProxy is
???????????? * over is selected. As the dragProxy moves, the Tree control's
???????????? * selection changes.
???????????? *
???????????? * For a bit more complication, the drop is being allowed
???????????? * only over nodes whose type is NOT 'state'.
???????????? * The feedback is removed.
???????????? */
??????????? private function onDragOver( event:DragEvent ) : void??????????? {
??????????????? var dropTarget:Tree = Tree(event.currentTarget);
??????????????? var r:int = dropTarget.calculateDropIndex(event);
??????????????? tree.selectedIndex = r;
??????????????? var node:XML = tree.selectedItem as XML;
??????????????? if( node.@type == "state" ) {??????????????????? DragManager.showFeedback(DragManager.NONE);
??????????????????? return;
??????????????? }
??????????????? if (event.ctrlKey)??????????????????? DragManager.showFeedback(DragManager.COPY);
??????????????? else if (event.shiftKey)
??????????????????? DragManager.showFeedback(DragManager.LINK);
??????????????? else {??????????????????? DragManager.showFeedback(DragManager.MOVE);
??????????????? }
??????????? }
??????????? /**
???????????? * Called when the dragProxy is released
???????????? * over the drop target. The information in the dragSource
???????????? * is extracted and processed.
???????????? *
???????????? * The target node is determined and
??????????? * all of the data selected (the List has allowMultipleSection
??????????? * set) is added.
???????????? */
??????????? private function onDragDrop( event:DragEvent ) : void??????????? {
??????????????? var ds:DragSource = event.dragSource;
??????????????? var dropTarget:Tree = Tree(event.currentTarget);
??????????????? var items:Array = ds.dataForFormat("items") as Array;
??????????????? var r:int = tree.calculateDropIndex(event);
??????????????? tree.selectedIndex = r;
??????????????? var node:XML = tree.selectedItem as XML;
??????????????? var p:*;
??????????????? // if the selected node has children (it is type==city),??????????????? // then add the items at the beginning
??????????????? if( tree.dataDescriptor.hasChildren(node) ) {
??????????????????? p = node;
??????????????????? r = 0;
??????????????? } else {??????????????????? p = node.parent();
??????????????? }
??????????????? for(var i:Number=0; i < items.length; i++) {??????????????????? var insert:XML = <node />;
??????????????????? insert.@label = items[i];
??????????????????? insert.@type? = "restaurant";
??????????????????? tree.dataDescriptor.addChildAt(p, insert, r+i);
??????????????? }??????????? }
??????????? /**
???????????? * Called when the drag operation completes, whether
???????????? * successfully or not. The tree is cleared of its
???????????? * selection.
???????????? */
??????????? private function onDragComplete( event:DragEvent ) : void??????????? {
??????????????? tree.selectedIndex = -1;
??????????? }???????
??????? ]]>
??? </mx:Script>???
??? <mx:XML id="treeData" xmlns="">??????? <root>
??????????? <node label="Massachusetts" type="state" data="MA">
??????????????? <node label="Boston" type="city" >
??????????????????? <node label="Smoke House Grill" type="restaurant" />
??????????????????? <node label="Equator" type="restaurant" />
??????????????????? <node label="Aquataine" type="restaurant" />??????????????????? <node label="Grill 23" type="restaurant" />
??????????????? </node>
??????????????? <node label="Provincetown" type="city" >
??????????????????? <node label="Lobster Pot" type="restaurant" />
??????????????????? <node label="The Mews" type="restaurant" />
??????????????? </node>??????????? </node>
??????????? <node label="California" type="state" data="CA">
??????????????? <node label="San Francisco" type="city" >
??????????????????? <node label="Frog Lane" type="restaurant" />
??????????????? </node>
??????????? </node>??????? </root>
??? </mx:XML>???
??? <mx:Array id="listData">
??????? <mx:String>Johnny Rocket's</mx:String>??????? <mx:String>Jet Pizza</mx:String>
??????? <mx:String>Steve's Greek</mx:String>
??????? <mx:String>Sonsie</mx:String>
??????? <mx:String>The Border Cafe</mx:String>??? </mx:Array>???
??? <mx:Panel x="48" y="125" width="447" height="351" layout="absolute" title="Drag onto Tree">???
??????? <mx:Tree width="186" left="10" top="10" bottom="10" id="tree"
??????????? labelField="@label"
??????????? dataProvider="{treeData.node}"
??????????? dropEnabled="false"
??????????? dragMoveEnabled="false"
??????????? dragEnter="onDragEnter(event)"
??????????? dragOver="onDragOver(event)"
??????????? dragDrop="onDragDrop(event)">??????? </mx:Tree>???????
??????? <mx:List width="188" height="206" right="10" bottom="10" id="list"
??????????? allowMultipleSelection="true"
??????????? dataProvider="{listData}"
??????????? dragEnabled="true"
??????????? dragMoveEnabled="true"
??????????? dragComplete="onDragComplete(event)">??????? </mx:List>???????
??????? <mx:Text x="229" y="10" text="Drag from the list below to the tree" width="188" height="39"/>??????? <mx:Label x="229" y="69" text="restaurants"/>
??? </mx:Panel>???
</mx:Application>
?结果