不用说什么了,需要两个文件:
CheckTree.as
package ht { import mx.controls.Tree; import mx.core.ClassFactory; import mx.events.ListEvent; /** * 三状态复选框树控件 * <br /><br /> */ public class CheckTree extends Tree { //数据源中状态字段 private var m_checkBoxStateField:String="@state"; //部分选中的填充色 [Bindable] private var m_checkBoxBgColor:uint=0x009900; //填充色的透明度 [Bindable] private var m_checkBoxBgAlpha:Number=1; //填充色的边距 [Bindable] private var m_checkBoxBgPadding:Number=3; //填充色的四角弧度 [Bindable] private var m_checkBoxBgElips:Number=2; //取消选择是否收回子项 [Bindable] private var m_checkBoxCloseItemsOnUnCheck:Boolean=true; //选择项时是否展开子项 [Bindable] private var m_checkBoxOpenItemsOnCheck:Boolean=false; //选择框左边距的偏移量 [Bindable] private var m_checkBoxLeftGap:int=8; //选择框右边距的偏移量 [Bindable] private var m_checkBoxRightGap:int=20; //是否显示三状态 [Bindable] private var m_checkBoxEnableState:Boolean=true; //与父项子项关联 [Bindable] private var m_checkBoxCascadeOnCheck:Boolean=true; //双击项目 public var itemDClickSelect:Boolean=true; public function CheckTree() { super(); doubleClickEnabled=true; } override protected function createChildren():void { var myFactory:ClassFactory=new ClassFactory(CheckTreeRenderer); this.itemRenderer=myFactory; super.createChildren(); addEventListener(ListEvent.ITEM_DOUBLE_CLICK, onItemDClick); } public function PropertyChange():void { dispatchEvent(new ListEvent(mx.events.ListEvent.CHANGE)); } /** * 树菜单,双击事件 * @param evt 双击事件源 * */ public function onItemDClick(e:ListEvent):void { if(itemDClickSelect) OpenItems(); } /** * 打开Tree节点函数,被 有打开节点功能的函数调用 * @param item 要打开的节点 * */ public function OpenItems():void { if (this.selectedIndex >= 0 && this.dataDescriptor.isBranch(this.selectedItem)) this.expandItem(this.selectedItem, !this.isItemOpen(this.selectedItem), true); } /** * 数据源中状态字段 * @return * */ [Bindable] public function get checkBoxStateField():String { return m_checkBoxStateField; } public function set checkBoxStateField(v:String):void { m_checkBoxStateField=v; PropertyChange(); } /** * 部分选中的填充色 * @return * */ [Bindable] public function get checkBoxBgColor():uint { return m_checkBoxBgColor; } public function set checkBoxBgColor(v:uint):void { m_checkBoxBgColor=v; PropertyChange(); } /** * 填充色的透明度 * @return * */ [Bindable] public function get checkBoxBgAlpha():Number { return m_checkBoxBgAlpha; } public function set checkBoxBgAlpha(v:Number):void { m_checkBoxBgAlpha=v; PropertyChange(); } /** * 填充色的边距 * @return * */ [Bindable] public function get checkBoxBgPadding():Number { return m_checkBoxBgPadding; } public function set checkBoxBgPadding(v:Number):void { m_checkBoxBgPadding=v; PropertyChange(); } /** * 填充色的四角弧度 * @return * */ [Bindable] public function get checkBoxBgElips():Number { return m_checkBoxBgElips; } public function set checkBoxBgElips(v:Number):void { m_checkBoxBgElips=v; PropertyChange(); } /** * 取消选择是否收回子项 * @return * */ [Bindable] public function get checkBoxCloseItemsOnUnCheck():Boolean { return m_checkBoxCloseItemsOnUnCheck; } public function set checkBoxCloseItemsOnUnCheck(v:Boolean):void { m_checkBoxCloseItemsOnUnCheck=v; PropertyChange(); } /** * 选择项时是否展开子项 * @return * */ [Bindable] public function get checkBoxOpenItemsOnCheck():Boolean { return m_checkBoxOpenItemsOnCheck; } public function set checkBoxOpenItemsOnCheck(v:Boolean):void { m_checkBoxOpenItemsOnCheck=v; PropertyChange(); } /** * 选择框左边距的偏移量 * @return * */ [Bindable] public function get checkBoxLeftGap():int { return m_checkBoxLeftGap; } public function set checkBoxLeftGap(v:int):void { m_checkBoxLeftGap=v; PropertyChange(); } /** * 选择框右边距的偏移量 * @return * */ [Bindable] public function get checkBoxRightGap():int { return m_checkBoxRightGap; } public function set checkBoxRightGap(v:int):void { m_checkBoxRightGap=v; PropertyChange(); } /** * 是否显示三状态 * @return * */ [Bindable] public function get checkBoxEnableState():Boolean { return m_checkBoxEnableState; } public function set checkBoxEnableState(v:Boolean):void { m_checkBoxEnableState=v; PropertyChange(); } /** * 与父项子项关联 * @return * */ [Bindable] public function get checkBoxCascadeOnCheck():Boolean { return m_checkBoxCascadeOnCheck; } public function set checkBoxCascadeOnCheck(v:Boolean):void { m_checkBoxCascadeOnCheck=v; PropertyChange(); } } }
CheckTreeRenderer.as
package ht { import <b>f</b>lash.events.MouseEvent; import <b>f</b>lash.geom.Rectangle; import <b>f</b>lash.xml.*; import mx.collections.*; import mx.controls.CheckBox; import mx.controls.Tree; import mx.controls.listClasses.*; import mx.controls.treeClasses.*; import mx.events.FlexEvent; import mx.events.ListEvent; /** * 三状态复选框树控件 * <br /><br /> */ public class CheckTreeRenderer extends TreeItemRenderer { protected var myCheckBox:CheckBox; /** * STATE_SCHRODINGER : 部分子项选中 <br /> * STATE_CHECKED : 全部子项选中 <br /> * STATE_UNCHECKED : 全部子项未选中 <br /> */ static private var STATE_SCHRODINGER:int=2; static private var STATE_CHECKED:int=1; static private var STATE_UNCHECKED:int=0; private var myTree:CheckTree; public function CheckTreeRenderer() { super(); mouseEnabled=true; } /** * 初始化完成时处理复选框和图片对象 * */ override protected function createChildren():void { myCheckBox=new CheckBox(); addChild(myCheckBox); myCheckBox.addEventListener(MouseEvent.CLICK, checkBoxToggleHandler); myTree = this.owner as CheckTree; super.createChildren(); myTree.addEventListener(ListEvent.CHANGE,onPropertyChange); } protected function onPropertyChange(e:ListEvent=null):void { this.updateDisplayList(unscaledWidth,unscaledHeight); } /** * // TODO : 递归设置父项目的状态 * @param item 项目 * @param tree 树对象 * @param state 目标状态 * */ private function toggleParents(item:Object, tree:Tree, state:int):void { if (item == null) return ; else { var stateField:String=myTree.checkBoxStateField; var tmpTree:IList=myTree.dataProvider as IList; var oldValue:Number=item[stateField] as Number; var newValue:Number=state as Number; item[myTree.checkBoxStateField]=state; tmpTree.itemUpdated(item,stateField,oldValue,newValue); //item[myTree.checkBoxStateField]=state; var parentItem:Object=tree.getParentItem(item); if(null!=parentItem) toggleParents(parentItem, tree, getState(tree, parentItem)); } } /** * // TODO : 设置项目的状态和子项的状态 * @param item 项目 * @param tree 树对象 * @param state 目标状态 * */ private function toggleChildren(item:Object, tree:Tree, state:int):void { if (item == null) return ; else { var stateField:String=myTree.checkBoxStateField; var tmpTree:IList=myTree.dataProvider as IList; var oldValue:Number=item[stateField] as Number; var newValue:Number=state as Number; item[myTree.checkBoxStateField]=state; tmpTree.itemUpdated(item,stateField,oldValue,newValue); var treeData:ITreeDataDescriptor=tree.dataDescriptor; if (myTree.checkBoxCascadeOnCheck && treeData.hasChildren(item)) { var children:ICollectionView=treeData.getChildren(item); var cursor:IViewCursor=children.createCursor(); while(!cursor.afterLast) { toggleChildren(cursor.current, tree, state); cursor.moveNext(); } } } } /** * //TODO:获得parent的状态 * @param tree 树对象 * @param parent 目标项 * @return 状态 * */ private function getState(tree:Tree, parent:Object):int { var noChecks:int=0; var noCats:int=0; var noUnChecks:int=0; if (parent != null) { var treeData:ITreeDataDescriptor=tree.dataDescriptor; var cursor:IViewCursor=treeData.getChildren(parent).createCursor(); while(!cursor.afterLast) { if (cursor.current[myTree.checkBoxStateField] == STATE_CHECKED) noChecks++; else if (cursor.current[myTree.checkBoxStateField] == STATE_UNCHECKED) noUnChecks++; else noCats++; cursor.moveNext(); } } if ((noChecks > 0 && noUnChecks > 0) || noCats > 0) return STATE_SCHRODINGER; else if (noChecks > 0) return STATE_CHECKED; else return STATE_UNCHECKED; } /** * //TODO:设置项目的父项状态和子项状态 * @param event 事件 * */ private function checkBoxToggleHandler(event:MouseEvent):void { if (data) { var myListData:TreeListData=TreeListData(this.listData); var selectedNode:Object=myListData.item; myTree=myListData.owner as CheckTree; var toggle:Boolean=myCheckBox.selected; if (toggle) { toggleChildren(data, myTree, STATE_CHECKED); if (myTree.checkBoxOpenItemsOnCheck) myTree.expandChildrenOf(data, true); } else { toggleChildren(data, myTree, STATE_UNCHECKED); if (myTree.checkBoxCloseItemsOnUnCheck) myTree.expandChildrenOf(data, false); } //TODO:如果所有子项选中时需要选中父项则执行以下代码 if (myTree.checkBoxCascadeOnCheck) { var parent:Object=myTree.getParentItem(data); if(null!=parent) toggleParents(parent, myTree, getState(myTree, parent)); } } //myTree.PropertyChange(); //dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); } /** * 设置本项的复选框状态 * @param checkBox 复选框 * @param value * @param state 状态 * */ private function setCheckState(checkBox:CheckBox, value:Object, state:int):void { if (state == STATE_CHECKED) checkBox.selected=true; else if (state == STATE_UNCHECKED) checkBox.selected=false; else if (state == STATE_SCHRODINGER) checkBox.selected=false; } override public function set data(value:Object):void { if (value != null) { super.data=value; setCheckState(myCheckBox, value, value[myTree.checkBoxStateField]); } } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if (super.data) { if (super.icon != null) { myCheckBox.x=super.icon.x + myTree.checkBoxLeftGap; myCheckBox.y=(height - myCheckBox.height) / 2; super.icon.x=myCheckBox.x + myCheckBox.width + myTree.checkBoxRightGap; super.label.x=super.icon.x + super.icon.width + 3; } else { myCheckBox.x=super.label.x + myTree.checkBoxLeftGap; myCheckBox.y=(height - myCheckBox.height) / 2; super.label.x=myCheckBox.x + myCheckBox.width + myTree.checkBoxRightGap; } setCheckState(myCheckBox, data, data[myTree.checkBoxStateField]); if (myTree.checkBoxEnableState && data[myTree.checkBoxStateField] == STATE_SCHRODINGER) { fillCheckBox(true); trace(myTree.checkBoxEnableState); trace(data[myTree.checkBoxStateField]); } else fillCheckBox(false); } } protected function fillCheckBox(isFill:Boolean):void { myCheckBox.graphics.clear(); if (isFill) { var myRect:Rectangle=getCheckTreeBgRect(myTree.checkBoxBgPadding); myCheckBox.graphics.beginFill(myTree.checkBoxBgColor, myTree.checkBoxBgAlpha) myCheckBox.graphics.drawRoundRect(myRect.x, myRect.y, myRect.width, myRect.height, myTree.checkBoxBgElips, myTree.checkBoxBgElips); myCheckBox.graphics.endFill(); } } protected function getCheckTreeBgRect(checkTreeBgPadding:Number):Rectangle { var myRect:Rectangle=myCheckBox.getBounds(myCheckBox); myRect.top+=checkTreeBgPadding; myRect.left+=checkTreeBgPadding; myRect.bottom-=checkTreeBgPadding; myRect.right-=checkTreeBgPadding; return myRect; } } //end class } //end package
测试文档:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" fontSize="12" xmlns:controls="ht.*" creationComplete="initApp()" layout="horizontal"> <mx:Script> <!--[CDATA[ import mx.collections.*; import ht.*; import mx.collections.ArrayCollection; [Bindable] public var arrarc:ArrayCollection=new ArrayCollection([ {state:0,label:"有效值"}, {state:0,label:"平均值"}, {state:0,label:"峰值"}, {state:0,label:"峰峰值"}, {state:0,label:"X1"}, {state:2,label:"频带", children:[ {state:0, label:"频带1"}, {state:1, label:"频带2"}, {state:0, label:"频带3"}, {state:0, label:"频带4"}, {state:0, label:"频带5"}, ] } ]); [Bindable] public var folderList:XMLList= <> <folder state="0" label="Marketing Collateral"> <folder state="0" label="Media,PR,and Communications" > <folder state="0" label="Article Reprint Disclaimers" /> <folder state="0" label="Articles Reprints" /> <folder state="0" label="Interviews and Transcripts" /> <folder state="0" label="Press Kits" /> <folder state="0" label="Press Releases" /> <folder state="0" label="Quick Hits" /> <folder state="0" label="Rep Talking Points" /> <folder state="0" label="Special Updates" /> <folder state="0" label="White Papers" /> </folder> <folder state="0" label="Forms and Applications" > <folder state="0" label="Applications" /> <folder state="0" label="Forms" /> </folder> </folder> </> ; [Bindable] public var folderCollection:XMLListCollection; override protected function initializationComplete():void { super.initializationComplete(); folderCollection=new XMLListCollection(folderList); } protected function initApp():void { this.callLater(test); } protected function test():void { tree3.checkBoxOpenItemsOnCheck=true; tree3.checkBoxCascadeOnCheck=true; tree3.checkBoxEnableState=false; tree3.itemDClickSelect=true; tree2.checkBoxOpenItemsOnCheck=true; tree2.checkBoxCascadeOnCheck=true; tree2.checkBoxEnableState=true; tree2.checkBoxBgColor=0x000000; } ]]--> </mx:Script> <mx:Panel width="166" height="100%" layout="absolute"> <mx:Form x="0" y="0" width="146" height="100%"> <mx:CheckBox label="取消收回子项" id="checkBoxCloseItemsOnUnCheck" selected="true"/> <mx:CheckBox label="选中展开子项" id="checkBoxOpenItemsOnCheck"/> <mx:CheckBox label="是否三状态" id="checkBoxEnableState" selected="true"/> <mx:CheckBox label="是否关联父子" id="checkBoxCascadeOnCheck" selected="true"/> <mx:FormItem label="样式" direction="vertical"> <mx:ColorPicker id="checkBoxBgColor" selectedColor="#009900"/> <mx:HSlider width="61" id="checkBoxBgAlpha" allowTrackClick="true" minimum="0" maximum="1" snapInterval="0.1" value="1"/> </mx:FormItem> <mx:FormItem label="填充边距"> <mx:NumericStepper id="checkBoxBgPadding" value="3" minimum="0" maximum="6" stepSize="1"/> </mx:FormItem> <mx:FormItem label="填充弧度"> <mx:NumericStepper id="checkBoxBgElips" value="2" minimum="0" maximum="6" stepSize="1"/> </mx:FormItem> <mx:FormItem label="左边距"> <mx:NumericStepper id="checkBoxLeftGap" value="8" minimum="0" maximum="20" stepSize="1"/> </mx:FormItem> <mx:FormItem label="右边距"> <mx:NumericStepper id="checkBoxRightGap" value="20" minimum="0" maximum="40" stepSize="1"/> </mx:FormItem> <mx:CheckBox label="双击是否展开项" id="itemDClickSelect" selected="true"/> </mx:Form> <mx:ControlBar height="46" y="321"> <mx:ToggleButtonBar> <mx:dataProvider> <mx:Array> <mx:String>Flash</mx:String> <mx:String>Director</mx:String> <mx:String>Dreamweaver</mx:String> <mx:String>ColdFusion</mx:String> </mx:Array> </mx:dataProvider></mx:ToggleButtonBar> </mx:ControlBar> </mx:Panel> <controls:CheckTree id="tree1" checkBoxStateField="@state" labelField="@label" dataProvider="{folderCollection}" width="100%" height="100%" checkBoxCloseItemsOnUnCheck="{checkBoxCloseItemsOnUnCheck.selected}" checkBoxOpenItemsOnCheck="{checkBoxOpenItemsOnCheck.selected}" checkBoxEnableState="{checkBoxEnableState.selected}" checkBoxCascadeOnCheck="{checkBoxCascadeOnCheck.selected}" checkBoxBgColor="{checkBoxBgColor.selectedColor}" checkBoxBgAlpha="{checkBoxBgAlpha.value}" checkBoxBgPadding="{checkBoxBgPadding.value}" checkBoxBgElips="{checkBoxBgElips.value}" checkBoxLeftGap="{checkBoxLeftGap.value}" checkBoxRightGap="{checkBoxRightGap.value}" itemDClickSelect="{itemDClickSelect.selected}" /> <controls:CheckTree id="tree2" width="100%" labelField="label" checkBoxStateField="state" dataProvider="{arrarc}" height="100%"/> <controls:CheckTree id="tree3" width="100%" labelField="label" checkBoxStateField="state" dataProvider="{arrarc}" height="100%"/> </mx:Application>
测试的效果图:
测试工程[下载地址]