FLEX 構建完美的帶有CheckBox三狀態的Tree控件

CheckTree.as

package ht.system.controls
{
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.system.controls
{
import <a title="<a title="<a title="<a title="Flash" href="http://" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>.events.MouseEvent;
import <a title="<a title="<a title="<a title="Flash" href="http://" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>.geom.Rectangle;
import <a title="<a title="<a title="<a title="Flash" href="http://" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>" href="http://" mce_href="../../undefined/" target="_blank">Flash</a>.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

test

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:ns1="controls.*"
fontSize="12"
xmlns:controls="ht.system.controls.*"
creationComplete="initApp()"
layout="horizontal" xmlns:ns2="flexlib.controls.*" xmlns:ns3="flexlib.containers.*" xmlns:ns4="ht.charts.controls.*" xmlns:ns5="ht.machine.views.*">
<mx:Script>
<!--[CDATA[
import mx.collections.*;
import ht.system.controls.*;

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>

轉自:http://blog.csdn.net/cjy37/archive/2009/05/11/4166621.aspx
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章