在Tree上添加CheckBox(II) – 支持级联选择
以前也写过一遍关于Tree CheckBox ItemRenderer的文章,分析如何在Tree的节点上添加Checkbox,实现了在节点上添加了CheckBox并关联对应数据的selected:Boolean属性,也就是CheckBox的选择状态随selected属性的改变而改变,selected也随时更新当前节点上CheckBox的选择状态。
目前这个版本不仅实现了原有的功能,而且支持了树结构级联选择:支节点选中的话支节点下的子节点也会选中。
效果图:

示例
设计思想:为了保证数据格式的合法性和通用性,要求数据源里的每个数据点都是强类型并且实现IListItemData接口,这样在ItemRenderer内部就可以统一对接口编程,程序也更加OOP。
接口IListItemData:
package org.beasy.controls.listClasses { import mx.collections.ArrayCollection; /** * @author Marco */ public interface IListItemData { /** * 唯一索引 */ function set id( value:String ):void; function get id():String; /** * 显示标签 */ function set label( value:String ):void; function get label():String; /** * 当前项是否选中 */ function set selected( value:Boolean ):void; function get selected():Boolean; /** * 当前荐是否可选 */ function set selectable( value:Boolean ):void; function get selectable():Boolean; /** * 当前子集 */ function set children( value:ArrayCollection ):void; function get children():ArrayCollection; /** * 当前项的父级 */ function get parent():IListItemData; } }
接口定义了一些最基本的属性其中要主意的属性是parent, parent属性是存放父级数据点的引用,顶级节点为null,这个属性在定义数据源的时候不要忘记赋值,因为级联选择就靠他了。
主类CheckBoxItemRendererPlus:
package org.beasy.controls.treeClasses { import flash.events.Event; import mx.binding.utils.BindingUtils; import mx.collections.ArrayCollection; import mx.controls.CheckBox; import mx.controls.treeClasses.TreeItemRenderer; import mx.controls.treeClasses.TreeListData; import org.beasy.controls.listClasses.IListItemData; import org.beasy.events.ItemRendererEvent; /** * 增强的在Tree组件上使用的ItemRenderer * 支持级联选中,反选,数据源同步. * * @author Marco */ public class CheckBoxTreeRendererPlus extends TreeItemRenderer { public function CheckBoxTreeRendererPlus() { super(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ protected var checkBox:CheckBox = null; /** * @private */ private var _dataDirty:Boolean; //-------------------------------------------------------------------------- // // Overridens // //-------------------------------------------------------------------------- /** * @protected * * 重写父类setter data方法 */ override public function set data(value:Object) : void { _dataDirty = true; invalidateProperties(); super.data = value; } /** * @protected * * 重写父类的<code>createChildren()</code>方法 * 在此方法内创建checkBox实例. */ override protected function createChildren() : void { super.createChildren(); checkBox = new CheckBox(); checkBox.addEventListener(Event.CHANGE, changeHandler); addChild( checkBox ); } /** * @protected * * 重写父类的<code>commitProperties()</code>方法 * 在此方法内进行发生绑定. */ override protected function commitProperties() : void { super.commitProperties(); if( _dataDirty ) { _dataDirty = false; var item:IListItemData = IListItemData( data ); BindingUtils.bindProperty(checkBox, "selected", item, "selected"); BindingUtils.bindProperty(checkBox, "enabled", item, "selectable"); } } /** * @protected * * 重写父类的<code>updateDisplayList()</code>方法 * 重新排列位置, 将label后移 */ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); var startx:Number = data ? TreeListData( listData ).indent : 0; if (disclosureIcon) { disclosureIcon.x = startx; startx = disclosureIcon.x + disclosureIcon.width; disclosureIcon.setActualSize(disclosureIcon.width, disclosureIcon.height); disclosureIcon.visible = data ? TreeListData( listData ).hasChildren : false; } if (icon) { icon.x = startx; startx = icon.x + icon.measuredWidth; icon.setActualSize(icon.measuredWidth, icon.measuredHeight); } if ( checkBox ) { checkBox.move(startx, ( unscaledHeight - checkBox.height ) / 2 ); label.x = startx + checkBox.getExplicitOrMeasuredWidth(); } } //-------------------------------------------------------------------------- // // Methodes // //-------------------------------------------------------------------------- /** * @private */ private function changeHandler( event:Event ):void { IListItemData(data).selected = checkBox.selected; var item:IListItemData = IListItemData( data ); checkChildren(item); checkPreLevel(item); var evt:ItemRendererEvent; if( !item.children && item.id != null ) { evt = new ItemRendererEvent( ItemRendererEvent.TREE_ITEM_CLICK, true); evt.item = item; evt.itemRenderer = this; dispatchEvent( evt ); } else if( item.children ) { var collection:ArrayCollection = new ArrayCollection(); getDescendant(item, collection); evt = new ItemRendererEvent( ItemRendererEvent.TREE_FOLDER_CLICK, true ); evt.children = collection; evt.itemRenderer = this; dispatchEvent( evt ); } } private function getDescendant( item:IListItemData, collection:ArrayCollection ):void { for each( var node:IListItemData in item.children ) { if( node.children ) { getDescendant(node, collection); } else { collection.addItem( node ); } } } /** * 递归算法,循环遍历所有子集 */ private function checkChildren(item:IListItemData):void { for each( var node:IListItemData in item.children ) { if (node.selected != item.selected) node.selected = item.selected; if( node.children ) checkChildren( node ); } } /** * 递归当前项父级 */ private function checkPreLevel(item:IListItemData):void { if( !item.parent ) return; var flag:Boolean = true; for each( var node:IListItemData in item.parent.children ) { if( !node.selected ) { flag = false; break; } } if( item.parent && flag != item.parent.selected ) { item.parent.selected = flag; checkPreLevel( item.parent ); } } } }
测试页面:
<?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/halo" minWidth="1024" minHeight="768" creationComplete="init()"> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import org.beasy.controls.listClasses.IListItemData; import org.beasy.controls.treeClasses.BasicTreeItemData; private var collection:ArrayCollection; private function init():void { initDatas(); tree.dataProvider = collection; } /** * 转换数据 */ private function initDatas():Boolean { collection = new ArrayCollection(); var xmlList:XMLList = dataSource.node; convertData( xmlList, collection, null ); return true; } /** * 将xml格式数据转换为ArrayCollection. */ private function convertData( xmlList:XMLList, collection:ArrayCollection, parent:IListItemData):void { for each( var node:XML in xmlList ) { var children:XMLList = node.elements("node"); var item:IListItemData = new BasicTreeItemData(parent); item.label = node.@label; if( children.length() > 0 ) { var childCollection:ArrayCollection = new ArrayCollection(); convertData(children, childCollection, item); item.children = childCollection; } collection.addItem( item ); } } ]]> </fx:Script> <fx:Declarations> <fx:XML id="dataSource"> <root> <node label="中国"> <node label="上海"> <node label="静安区"/> <node label="黄浦区"/> <node label="浦东区"/> </node> <node label="北京"> <node label="海淀区"/> <node label="朝阳区"/> </node> </node> </root> </fx:XML> </fx:Declarations> <mx:Tree id="tree" width="200" height="400" x="20" y="20" itemRenderer="org.beasy.controls.treeClasses.CheckBoxTreeRendererPlus"/> </s:Application>
全部代码请查看: http://code.google.com/p/beasy-flex-library/ 这里包含了本博所有文章源码.
SVN: http://beasy-flex-library.googlecode.com/svn/trunk/
多谢楼主的分享 提点小建议 如果可以使用xmllist 而不需要转换的话就更好了
楼主写得很好,必须得支持一下
This specific is probably the nearly all respected discussions I ever mastered within a long time, Now i’m uttering with this element of your posting “