在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 &amp;&amp; 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 &amp;&amp; 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/

相关日志

  1. passi0n 说道:

    多谢楼主的分享 提点小建议 如果可以使用xmllist 而不需要转换的话就更好了

  2. 肖邦 说道:

    楼主写得很好,必须得支持一下

  3. 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 “

  1. 纬度网 说道:

    在Tree上添加CheckBox(II) – 支持级联选择…

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

添加评论