完美的Tree
如果是层次化数据则无法使用线性列表呈现,此时Tree是首要考虑的组件,Tree基于List,使用图标和缩进显示层级。Tree是单列组件,在行列显示大小,显示行数机制上均与List相同,只是Tree拥有展开文件夹的功能,下面我们来看看Tree在List上作了哪些扩展。
Tree的数据源
在选择合适的集合类型中我们讲过,XMLListCollection对层级数据具有优势,因此常常使用XMLListCollection作为Tree的数据源,其它数据源会当作Object类型且必须满足下面两个条件:
1.???必须有children字段
2.???children符合转化为可视数据集合的条件
这是由Tree默认的数据分析器DefaultDataDescriptor决定的,因此在现有数据集合中除XMLListCollection外只有Model符合条件,要使用ArrayCollection,必须对ArrayCollection进行扩展。下面我们绑定这3个数据集合的情况。
绑定XMLListCollection
XMLListCollection为Tree组件推荐数据源,因此在使用上非常方便,Tree自动分析XMLListCollection的层级关系并自动生成枝和叶,若你想偷懒省去XML转化为XMLListCollection的过程,Tree还为此提供了特别的照顾,showRoot:Boolean属性可以决定是否显示XML的根节点,大多情况下我们无需显示根节点以节省显示空间,因此应该将showRoot设置为false(默认为true)。下面使用Tree直接显示一个XML文件的结构:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> ??? <mx:Tree id="myTree" showRoot="false" labelField="@label"> ?????? <mx:XML source="administratives.xml"/> ??? </mx:Tree> </mx:Application> |
上述代码虽然异常简洁,但却不是推荐的做法,原因有3点:
1.????? 除非显示固有数据,否则建议使用绑定。
2.????? 不建议绑定原始对象而应该进行手动转化成XMLListionCollection。
3.????? 直接绑定XML是需要额外将showRoot设置为false,因为showRoot默认为true。
因此推荐的做法是手动将数据源转化为XMLListCollection后进行绑定,修改代码如下:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> ??? <mx:XML id="xmlfile" source="administratives.xml"/> ??? <mx:XMLListCollection id="administratives" source="{xmlfile.nation}"/> ??? <mx:Tree id="myTree" dataProvider="{administratives}" showRoot="false" labelField="@label"/> </mx:Application> |
绑定Model
只需将节点都定义为<children>标签就能满足DefaultDataDescriptor解析的条件,例如:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> ??? <mx:Object id="myObj" label="root"> ?????? <mx:children><mx:Object label="layer1"> ????????????? <mx:children><mx:Object label="layer2"> ???????????????????? <mx:Sprite /> ????????????? </mx:Object></mx:children> ?????? </mx:Object></mx:children> ??? </mx:Object> ??? <mx:Tree id="myTree" dataProvider="{myObj}" showRoot="true" width="181"/> </mx:Application> |
当扫描Model的children字段时,Model会生成包含所有子标签的数组,这刚好符合第2个条件。使用Model需将showRoot属性设置为false。
绑定ArrayCollection
ArrayCollection本身不建议描述层级数组,ArrayCollection也不符合DefaultDataDescriptor分析的条件,要绑定Tree,一种方法是自定义数据分析器直接遍历数组,另一种方法是扩展ArrayCollection满足默认数据分析器的条件,鉴于ArrayCollection在深度绑定方面的缺陷,自定义数据分析器亦不能解决深度绑定,因此采用第二种方法较为合适,此时可以把深度绑定和children字段都写入到扩展代码中,如下:
ArrayCollectionForTree.as
package { ??? import mx.collections.ArrayCollection; ??? import mx.events.CollectionEvent; ? ??? public class ArrayCollectionForTree extends ArrayCollection ??? { ?????? public var label:String; ?????? public var parent:ArrayCollectionForTree; ?????? public var children:ArrayCollectionForTree; ?????? ?????? ?????? override public function addItem(item:Object):void ?????? { ?????????? super.addItem(item); ?????????? if(item is ArrayCollectionForTree) ?????????? { ????????????? item.parent=this; ?????????? } ?????? } ?????? ?????? ?????? override public function addItemAt(item:Object,index:int):void ?????? { ?????????? super.addItemAt(item,index); ?????????? if(item is ArrayCollectionForTree) ?????????? { ????????????? item.parent=this; ?????????? } ?????? } ?????? ?????? ?????? public function ArrayCollectionForTree(source:Array=null) ?????? { ?????????? super(source); ?????????? children=this; ?????????? addEventListener(CollectionEvent.COLLECTION_CHANGE,notifitionParent); ?????? } ?????? ?????? ?????? private function notifitionParent(event:CollectionEvent):void ?????? { ?????????? if(parent) ?????????? { ????????????? parent.itemUpdated(this); ?????????? } ?????? } ??? } } |
test.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> ??? <mx:Script> ?????? <![CDATA[ ?????????? [Bindable] ?????????? public var myArr:ArrayCollectionForTree=new ArrayCollectionForTree(); ?????????? [Bindable] ?????????? public var arr1:ArrayCollectionForTree=new ArrayCollectionForTree([1,2,3]); ?????????? [Bindable] ?????????? public var arr2:ArrayCollectionForTree=new ArrayCollectionForTree([4,5,6]); ?????????? ?????????? private function treeCreationComplete():void ?????????? { ????????????? arr1.label="arr1"; ????????????? myArr.addItem(arr1); ????????????? ????????????? arr2.label="arr2"; ????????????? myArr.addItem(arr2); ?????????? } ?????? ]]> ??? </mx:Script> ??? <mx:Tree id="myTree" dataProvider="{myArr}" width="181" creationComplete="treeCreationComplete()"/> ??? <mx:Button id="testButton" click="arr1.addItem(7)"? y="174"/> </mx:Application> |
在ArrayCollectionForTree中对ArrayCollection进行了简单的扩展,增加的children属性用于满足DefaultDataDescriptor的解析需求,children指向自身,DefaultDataDescriptor无需进行数据集合的转化。parent属性连接父级别,在子级更改时通知父级更新。这里没有设置showRoot,但Tree并未显示根节点,因为showRoot对于线性数据是不起作用的。
显示根节点
showRoot属性只对具有根节点的数据源有效,是否是线性数据通过hasRoot:Boolean属性进行判断,hasRoot当数据类型是Array,ArrayCollection,XMLList,XMLListCollection时返回flase,如果不是扩展ArrayCollection,而是将children属性定义为ArrayCollection是可以显示根节点的,因为Tree将普通数据类型当作Object,Object是看作有根节点的数据源。
具有根节点的数据源虽然要惦记着将showRoot属性设置为false以节省显示空间,但优点是可以通过expandChildrenOf()方法一次打开或关闭所有的分枝。
空分支
DefaultDataDescriptor为数据集合提供了创建空分支的方式,对于XMLListCollection可以通过增加标签属性isBranch=”true”来定义空节点,非XMLListCollection以是否拥有children字段为分支的标志,即便children中子级个数为0。
TreeListData
TreeListData记录的单元信息非常重要,一方面层级数据是没有索引的,ListBase提供的selectedIndex在进行层级定位或获取条目层级信息时(例如枝还是叶、所在层级等)无济于事,自己编写代码在数据源中检索selectedItem是吃力不讨好的手段。TreeListData提供的单元信息类型提供了描述层级的详细信息:
属性 |
说明 |
depth:int |
当前层级 |
hasChildren:Boolean |
是否包含子节点 |
open:Boolean |
是否已打开 |
icon:Class |
当前图标类 |
disclosureIcon:Class |
三角图标类 |
indent:int |
Tree 控件的此行的默认缩进 |
item:Object |
对应的数据条目,等同于TreeItemRenderer的data属性 |
这份清单提供了我们需要的条目信息以及单元渲染器信息,这些信息随着数据源更新而更新,下例点击每个节点后对照TreeListData信息和这两个选择属性:
<?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.treeClasses.TreeItemRenderer; ????????? import mx.controls.treeClasses.TreeListData; ????????? import mx.events.ListEvent; ????????? private function showDataInfo(event:ListEvent):void ????????? { ????????????? baseText.text="selectIndex="+myTree.selectedIndex; ????????????? baseText.text+="nselectedItem="+myTree.selectedItem; ????????????? ????????????? var data:TreeListData=TreeItemRenderer(event.itemRenderer).listData as TreeListData; ????????????? dataText.text="depth="+data.depth+"n"; ????????????? dataText.text+="hasChildren="+data.hasChildren+"n"; ????????????? dataText.text+="open="+data.open+"n"; ????????????? dataText.text+="icon="+data.icon+"n"; ????????????? dataText.text+="disclosureIcon="+data.disclosureIcon+"n"; ????????????? dataText.text+="indent="+data.indent+"n"; ????????????? dataText.text+="item="+data.item; ????????? } ?????? ]]> ?? </mx:Script> ?? <mx:XMLList id="treeData"> ?????? <node label="Mail Box"> ????????? <node label="Inbox"> ????????????? <node label="Marketing"/> ????????????? <node label="Product Management"/> ????????????? <node label="Personal"/> ????????? </node> ????????? <node label="Outbox"> ????????????? <node label="Professional"/> ????????????? <node label="Personal"/> ????????? </node> ????????? <node label="Spam"/> ????????? <node label="Sent"/> ?????? </node> ?? </mx:XMLList> ?? <mx:Tree id="myTree" dataProvider="{treeData}" labelField="@label" change="showDataInfo(event)" ?????? width="249" x="445" y="48" height="285"/> ?? <mx:Panel title="ListBase" x="143" y="48" width="294" height="91"> ?????? <mx:TextArea id="baseText" width="100%" height="100%" /> ?? </mx:Panel> ?? <mx:Panel title="TreeDataList" x="143" y="147" width="294" height="186"> ?????? <mx:TextArea id="dataText" width="100%" height="100%" /> ?? </mx:Panel> </mx:Application> |
selectedIndex是折叠后的显示索引,等同于TreeListData的rowIndex属性,selectedItem等同于TreeItemRenderer的data或TreeListData的item属性,由此可看到selectedIndex和selectedItem在Tree中的地位。
TreeItemRenderer
TreeItemRenderer读取TreeListData记录的的单元信息进行渲染,创建条目图标信息,三角图标并且根据层级进行缩进,当文件夹打开时Tree会以列表方式插入或删除新的渲染器实例,然后更改与之相关联的渲染器的listData信息,这些渲染器根据listData更新图标和三角图标显示,例如将关闭的文件夹更换为打开,新的条目按层级显示缩进,实现了打开和关闭文件夹的视觉呈现。
listData的数据由两部分写入,labelField和iconField只能设置叶节点。文件夹图标、三角图标均由Tree的样式设置,如下:
defaultLeafIcon缺省文件图标
folderOpenIcon打开的文件夹图标
folderClosedIcon关闭的文件夹图标
disclosureClosedIcon文件夹关闭时的三角形
disclosureOpenIcon文件夹打开时的展开三角形。
这样设计的原因是文件夹图标和三角图标通常情况下应该是全局性质的,而文件图标当不指定iconField时不应该没有显示,而是显示defaultLeafIcon定义的缺省图标。下例制作一个具有分组的花名册面板:
<?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.treeClasses.TreeItemRenderer; ?????????? import mx.controls.treeClasses.TreeListData; ?????????? import mx.events.ListEvent; ?????????? ?????????? [Bindable] ?????????? [Embed("defaultMan.png")] ?????????? public var defaultMan:Class; ?????????? ?????????? [Bindable] ?????????? [Embed("defaultWoman.png")] ?????????? public var defaultWoman:Class; ?????????? ?????????? [Bindable] ?????????? [Embed("defaultHead.png")] ?????????? public var defaultHead:Class; ?????? ]]> ??? </mx:Script> ??? <mx:XMLList id="treeData"> ?????? <group label="我的好友" isBranch="true"> ?????????? <node label="王小建" icon="defaultMan" /> ?????????? <node label="何东平" icon="defaultWoman" /> ?????? </group> ?????? <group label="陌生人" isBranch="true"> ?????????? <node label="江北风" icon="defaultHead" /> ?????????? <node label="宋小雨" icon="defaultHead" /> ?????? </group> ?????? <group label="黑名单" isBranch="true"> ?????????? <node label="XX**XX" icon="defaultHead" /> ?????????? <node label="广告宣传" icon="defaultHead" /> ?????? </group> ??? </mx:XMLList> ??? <mx:Tree id="myTree" dataProvider="{treeData}" labelField="@label" iconField="@icon" folderOpenIcon="undefined" ?????? folderClosedIcon="undefined" width="214" x="10" y="10" height="352" rowHeight="45" fontSize="19"/> </mx:Application> |
将folderOpenIcon和folderCloseIcon设置为undefined时可以禁止文件夹图标显示,设置iconField字段定义每个叶节点的图标。如果你想为每个文件夹指定图标可以调用Tree的setItemIcon(item:Object,iconID:Class,iconID2:Class):void方法,item是数据条目,iconID和iconID2是关闭和打开的文件夹图标。
打开和关闭节点
除了点击三角箭头打开和关闭节点之外,还能使用代码打开和关闭节点:
expandItem(item:Object,open:Boolean,animate:Boolean = false,dispatchEvent:Boolean = false,cause:Event = null):void
打开或关闭指定的分支,item为分枝节点,对于叶节点无效,open为true时是打开节点,为false时关闭节点,animate为播放展开动画,dispatchEvent为true时会广播itemOpen和itemClose事件,cause是导致打开和关闭节点的事件,不一定存在。
expandChildrenOf(item:Object,open:Boolean):void
打开或关闭指定分枝并展开所有子节点。
要检查一个分枝节点是否被打开,可以调用isItemOpen(item:Object):Boolean方法,要返回叶节点的父项目,可以调用getParentItem(item:Object):*方法。
我们修改前面花名册的例子,禁用展开三角形图标而只能靠双击打开和关闭节点:
<?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.TreeEvent; ?????????? import mx.controls.treeClasses.TreeItemRenderer; ?????????? import mx.controls.treeClasses.TreeListData; ?????????? import mx.events.ListEvent; ?????????? ?????????? [Bindable] ?????????? [Embed("defaultMan.png")] ?????????? public var defaultMan:Class; ?????????? ?????????? [Bindable] ?????????? [Embed("defaultWoman.png")] ?????????? public var defaultWoman:Class; ?????????? ?????????? [Bindable] ?????????? [Embed("defaultHead.png")] ?????????? public var defaultHead:Class; ?????????? ?????????? ?????????? private function expandNode(event:ListEvent):void ?????????? { ????????????? var node:XML=TreeListData(TreeItemRenderer(event.itemRenderer).listData).item as XML; ????????????? if(myTree.isItemOpen(node)) ????????????? { ????????????????? myTree.expandItem(node,false); ????????????? } ????????????? else ????????????? { ????????????????? myTree.expandItem(node,true); ????????????? } ?????????? } ?????? ]]> ??? </mx:Script> ??? <mx:XMLList id="treeData"> ?????? <group label="我的好友" isBranch="true"> ?????????? <node label="王小建" icon="defaultMan" /> ?????????? <node label="何东平" icon="defaultWoman" /> ?????? </group> ?????? <group label="陌生人" isBranch="true"> ?????????? <node label="江北风" icon="defaultHead" /> ?????????? <node label="宋小雨" icon="defaultHead" /> ?????? </group> ?????? <group label="黑名单" isBranch="true"> ?????????? <node label="XX**XX" icon="defaultHead" /> ?????????? <node label="广告宣传" icon="defaultHead" /> ?????? </group> ??? </mx:XMLList> ??? <mx:Tree id="myTree" dataProvider="{treeData}" labelField="@label" iconField="@icon" folderOpenIcon="undefined" ?????? folderClosedIcon="undefined" disclosureOpenIcon="undefined" disclosureClosedIcon="undefined" width="214" x="10" ?????? y="10" height="352" rowHeight="45" fontSize="19" doubleClickEnabled="true" itemDoubleClick="expandNode(event)"/> </mx:Application> |
将disclosureOpenIcon和disclosureCloseIcon设置为undefined禁用三角图标后,开启Tree的节点双击功能,并使用isItemOpen检测状态后执行打开和关闭操作,当然你也可以通过渲染器单元信息来获取打开关闭状态。
Tree中的数据拖放
由于Tree是层级数据,因此不能和线性列表之间进行拖放Tree和Tree之间的数据拖放也不是很常见,比较常用的是Tree自身的节点的拖放,列表组件默认的拖放风格是显示下划线,在下划线的位置重新插入,现在我们想颠覆这种风格,使拖放操作看起来像qq面板风格,修改代码如下:
IMTree.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Tree xmlns:mx="http://www.adobe.com/2006/mxml" folderOpenIcon="undefined" folderClosedIcon="undefined" itemClick="expandNode(event)" dragEnabled="true" dropEnabled="true" dropIndicatorSkin="mx.controls.Spacer" dragStart="onDragStart(event)" dragEnter="onDragEnter(event)" dragOver="onDragOver(event)" dragExit="onDragExit(event)" dragDrop="onDragDrop(event)"> ??? <mx:Script> ?????? <![CDATA[ ?????????? import mx.managers.DragManager; ?????????? import mx.collections.XMLListCollection; ?????????? import mx.events.DragEvent; ?????????? import mx.events.TreeEvent; ?????????? import mx.controls.treeClasses.TreeItemRenderer; ?????????? import mx.controls.treeClasses.TreeListData; ?????????? import mx.events.ListEvent; ? ? ? ?????????? private function expandNode(event:ListEvent):void ?????????? { ????????????? var listData:TreeListData=TreeItemRenderer(event.itemRenderer).listData as TreeListData; ????????????? if(listData.hasChildren) ????????????? { ????????????????? var node:*=listData.item; ????????????????? if (isItemOpen(node)) ????????????????? { ???????????????????? expandItem(node,false); ????????????????? } ????????????????? else ????????????????? { ???????????????????? expandItem(node,true); ????????????????? } ????????????? } ?????????? } ? ? ? ?????????? private function onDragStart(event:DragEvent):void ?????????? { ????????????? var listData:TreeListData=getListDataOnMousePos(event); ????????????? if (listData && listData.hasChildren == false) ????????????? { ????????????????? expandChildrenOf(dataProvider[0],false); ????????????? } ????????????? else ????????????? { ????????????????? event.preventDefault(); ????????????? } ?????????? } ? ? ? ?????????? private function onDragEnter(event:DragEvent):void ?????????? { ????????????? if (event.dragInitiator != this) ????????????? { ????????????????? event.preventDefault(); ????????????? } ?????????? } ? ? ? ?????????? private function onDragOver(event:DragEvent):void ?????????? { ????????????? var listData:TreeListData=getListDataOnMousePos(event); ????????????? if (listData) ????????????? { ????????????????? var item:Object=listData.item; ????????????????? if (!isItemHighlighted(item)) ????????????????? { ???????????????????? drawItem(getItemRendenerOnMousePos(event),false,true); ????????????????? } ????????????? } ????????????? else ????????????? { ????????????????? clearIndicators(); ????????????????? event.preventDefault(); ????????????????? DragManager.showFeedback(DragManager.NONE); ????????????? } ?????????? } ? ? ? ?????????? private function onDragExit(event:DragEvent):void ?????????? { ????????????? clearIndicators(); ?????????? } ? ? ? ?????????? private function onDragDrop(event:DragEvent):void ?????????? { ????????????? event.preventDefault(); ????????????? var listData:TreeListData=getListDataOnMousePos(event); ????????????? if (listData && listData.hasChildren) ????????????? { ????????????????? var folder:XML=listData.item as XML; ????????????????? var node:XML=event.dragSource.dataForFormat("treeItems")[0]; ????????????????? var parent:XML=getParentItem(node)as XML; ????????????????? if (folder != parent) ????????????????? { ???????????????????? delete parent.*[node.childIndex()]; ???????????????????? folder.appendChild(node); ????????????????? } ????????????? } ?????????? } ? ? ? ?????????? private function getItemRendenerOnMousePos(event:DragEvent):TreeItemRenderer ?????????? { ????????????? var index:int=calculateDropIndex(event); ????????????? return indexToItemRenderer(index)as TreeItemRenderer; ?????????? } ? ? ? ?????????? private function getListDataOnMousePos(event:DragEvent):TreeListData ?????????? { ????????????? var itemRenderer:TreeItemRenderer=getItemRendenerOnMousePos(event); ? ????????????? if (itemRenderer) ????????????? { ????????????????? return itemRenderer.listData as TreeListData; ????????????? } ????????????? return null; ?????????? } ?????? ]]> ??? </mx:Script> </mx:Tree> |
这段代码是多个知识点的综合,我们对重点地方来详细讲解一下:
l???????删除黑线
默认拖放位置外观由dropIndicatorSkin样式决定,默认为ListDropIndicator类,是一条黑线,这个样式不能通过将样式设定为undefined来禁用,因此我们将其更改为看不见的Spacer。
l???????通过根节点关闭所有分组
我们利用具有根节点的数据源调用expandChildrenOf()方法,注意不能直接操作dataProvider,dataProvider将XML包装成包含一个节点的XMLListCollection,因此要取第一个节点dataProvider[0]。关闭所有分组在拖放流程中的第一步进行。
l???????限制拖放对象
在dragEnter事件中通过dragEvent的dragInitiator属性判断启动器是否为自身从而拒绝来自其它Tree的拖放操作,非Tree组件本身不能不可放置。
l???????拖动中高亮
由于没有了插入黑线,我们在dragOver使用高亮呈现拖放外观,drawItem(item:IListItemRenderer,selected:Boolean = false,highlighted:Boolean = false,caret:Boolean = false,transition:Boolean =false):void方法是ListBase保护方法,drawItem在内部绘制渲染器的选中,高亮,尖号状态,参数如下:
item???????? 渲染器引用
selected???? 应用选中状态
highlighted? 高亮状态
caret??????? 尖号状态
trasition??? 应用淡入淡出
不必执行还原高亮的代码,当鼠标掠出时ListBase会自动删除所有高亮效果。
在数据拖放中,MouseEvent被禁止,因此不能通过itemRollOver来获取放置位置,dragEvent中不提供放置的位置也不提供放置的条目,我们只能通过calculateDropIndex()方法来定位,然后自己寻找单元渲染器和条目数据,定位的渲染器不一定存在,因此一定要先判断。由于dragOver会当鼠标移动时频繁调用,因此通过isItemHighlighted(item:Object):Boolean方法进行过滤。
l?????????????????????????????????????????????编写自己的放置逻辑
Tree默认的放置操作是不能在文件夹关闭状态下放入节点的,如果不阻止默认行为,节点将会放置到文件夹的同级,我们通过Tree提供的getParentItem()方法获取节点父级后删除了节点本身。
Tree中的滚动
Tree可以横向滚动也可以纵向滚动,当展开的节点宽度超过显示范围时会出现横向滚动条,当展开分枝后行数不足以显示会出现纵向滚动条,由于层级数据无索引,Tree使用firstVisibleItem:Object属性设置可见的第一行。
Tree的事件
Tree在列表事件ListEvent上增加了TreeEvent事件专门处理节点的打开和关闭,类型有
TreeEvent.OPEN?????? 打开节点时触发
TreeEvent.CLOSE????? 关闭节点时触发
TreeEvent.OPENING??? 打开节点前触发
可以通过TreeEvent直接获取渲染器itemRenderer、数据条目item,打开状态opening和触发器triggerEvent。
自定义Tree渲染器
Tree的工作原理使得自定义Tree渲染器要比列表渲染器多做一些事情,从TreeListData和TreeItemREnderer来看,打开关闭文件夹时显示条目的增减由Tree执行,渲染器只需正确呈现TreeListData中对应的图标和做好缩进工作即可,以此我们借鉴TreeListData中的源码,制定自己的渲染器:
MyTreeItemRenderer.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" implements="mx.controls.listClasses.IDropInListItemRenderer" creationComplete="onCreationComplete()" dataChange="updateDisplay()"> ?? <mx:states> ?????? <mx:State name="group"> ????????? <mx:AddChild> ????????????? <mx:Image id="disclosureIcon" mouseDown="disclosureMouseDownHandler(event)"/> ????????? </mx:AddChild> ????????? <mx:AddChild> ????????????? <mx:Label id="groupName" /> ????????? </mx:AddChild> ?????? </mx:State> ?????? ?????? <mx:State name="contact"> ????????? <mx:AddChild> ????????????? <mx:Image id="contactIcon" /> ????????? </mx:AddChild> ????????? <mx:AddChild> ????????????? <mx:VBox> ???????????????? <mx:Label id="contactName" /> ???????????????? <mx:Label id="contactSign" /> ????????????? </mx:VBox> ????????? </mx:AddChild> ?????? </mx:State> ?? </mx:states> ?? <mx:Script> ?????? <![CDATA[ ????????? import mx.controls.Tree; ????????? import mx.events.TreeEvent; ????????? import mx.controls.listClasses.BaseListData; ????????? import mx.controls.treeClasses.TreeListData; ????????? import mx.core.mx_internal; ????????? ????????? use namespace mx_internal; ????????? ????????? ????????? private var _listData:TreeListData; ????????? private var listOwner:Tree; ? ? ? ????????? public function get listData():BaseListData ????????? { ????????????? return _listData; ????????? } ? ? ????????? [Bindable] ????????? public function set listData(value:BaseListData):void ????????? { ????????????? _listData=TreeListData(value); ????????????? listOwner=Tree(_listData.owner); ????????? } ? ? ? ????????? private function disclosureMouseDownHandler(event:Event):void ????????? { ????????????? event.stopPropagation(); ? ????????????? if (listOwner.isOpening || !listOwner.enabled) return; ? ????????????? var open:Boolean=_listData.open; ????????????? _listData.open=!open; ? ????????????? listOwner.dispatchTreeEvent(TreeEvent.ITEM_OPENING,_listData.item,? //item ???????????????? this,? //renderer ???????????????? event,? //trigger ???????????????? !open,? //opening ???????????????? true,? //animate ???????????????? true) ????????? } ????????? ????????? ????????? ????????? private function onCreationComplete():void ????????? { ????????????? //初始化完成后进行渲染 ????????????? updateDisplay(); ????????? } ????????? ????????? ????????? //渲染,如果是第一次创建则通过onCreationComplete方法调用,如果是从缓存中取出的则直接调用 ????????? private function updateDisplay():void ????????? { ????????????? //只有具有条目数据并且初始化完成后才允许渲染 ????????????? if (!(_listData && initialized)) return; ????????????? ????????????? if (_listData.hasChildren) ?????? ?????? { ???????????????? currentState="group"; ???????????????? setStyle("paddingLeft",0); ???????????????? if(disclosureIcon && disclosureIcon.source!=_listData.disclosureIcon) ???????????????? { ???????????????????? disclosureIcon.source=_listData.disclosureIcon; ???????????????? } ???????????????? if(groupName && groupName.text!=_listData.label) ???????????????? { ???????????????????? groupName.text=_listData.label; ???????????????? } ????????????? } ????????????? else if (!_listData.hasChildren) ????????????? { ???????????????? currentState="contact"; ???????????????? setStyle("paddingLeft",_listData.indent); ???????????????? if(contactIcon && contactIcon.source!=_listData.item.@head) ???????????????? { ????????? ?????????? contactIcon.source=_listData.item.@head; ???????????????? } ???????????????? if(contactName && contactName.text!=_listData.label) ???????????????? { ???????????????????? contactName.text=_listData.label ???????????????? } ???????????????? if(contactSign && contactSign.text!=_listData.item.@sign) ???????????????? { ???????????????????? contactSign.text=_listData.item.@sign; ???????????????? } ????????????? } ????????? } ?????? ]]> ?? </mx:Script> </mx:HBox> |
Test.mxml
<?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.treeClasses.TreeItemRenderer; ?????????? import mx.controls.treeClasses.TreeListData; ?????????? import mx.events.ListEvent; ?????????? private function showDataInfo(event:ListEvent):void ?????????? { ????????????? baseText.text="selectIndex="+myTree.selectedIndex; ????????????? baseText.text+="nselectedItem="+myTree.selectedItem.toXMLString(); ????????????? ????????????? var data:TreeListData=MyTreeItemRenderer(event.itemRenderer).listData as TreeListData; ????????????? dataText.text="depth="+data.depth+"n"; ????????????? dataText.text+="hasChildren="+data.hasChildren+"n"; ????????????? dataText.text+="open="+data.open+"n"; ????????????? dataText.text+="icon="+data.icon+"n"; ????????????? dataText.text+="disclosureIcon="+data.disclosureIcon+"n"; ????????????? dataText.text+="indent="+data.indent+"n"; ????????????? dataText.text+="item="+data.item; ?????????? } ?????? ]]> ??? </mx:Script> ??? <mx:XMLListCollection id="treeData"> ?????? <mx:XMLList xmlns=""> ?????????? <group name="我的好友" isBranch="true"> ?????????? <node name="王小建" head="defaultMan.png" sign="求存养息"/> ?????????? <node name="何东平" head="defaultWoman.png" sign="幸运到底有多重要"/> ?????????? </group> ?????????? <group name="陌生人" isBranch="true"> ????????????? <node name="江北风" head="defaultHead.png" sign="这是流亡的场所"/> ????????????? <node name="宋小雨" head="defaultHead.png" sign="诚信方能立身"/> ?????????? </group> ?????????? <group name="黑名单" isBranch="true"> ????????????? <node name="XX**XX" head="defaultHead.png" sign="冬天来了"/> ????????????? <node name="广告宣传" head="defaultHead.png" sign="www.ttq.com"/> ?????????? </group> ?????? </mx:XMLList> ??? </mx:XMLListCollection> ??? <mx:Tree id="myTree" dataProvider="{treeData}" labelField="@name" change="showDataInfo(event)" ?????? width="249" x="445" y="48" height="285" itemRenderer="MyTreeItemRenderer" variableRowHeight="true"/> ??? <mx:Panel title="ListBase" x="143" y="48" width="294" height="91"> ?????? <mx:TextArea id="baseText" width="100%" height="100%" /> ??? </mx:Panel> ??? <mx:Panel title="TreeDataList" x="143" y="147" width="294" height="186"> ?????? <mx:TextArea id="dataText" width="100%" height="100%" /> ??? </mx:Panel> </mx:Application> |
在这个例子中,我们将渲染器设计成分组和联系人两种状态从而选择性创建其外观,叶节点使用Spacer控制其缩进,当数据条目更改时根据当前状态进行设置子对象,此外将Tree的variableRowHeight属性设置为true让Tree适应不同的行高从而更像一个标准的IM面板。在信息面板中,可以发现传递到渲染器中的data和listData并没有改变,只是渲染方式不同罢了。