常常会用到带有3种状态CheckBox的树形组件,比如在权限管理中,或者是地区选择中等等,如下图:
不多说费话了,直接进入主题,看看如何实现。其实在Flex中,只用自己实现一个TreeItemRenderer就可以了,代码如下:
package com.robin
{
import flash.events.Event;
import flash.geom.Rectangle;
import mx.controls.CheckBox;
import mx.controls.treeClasses.TreeItemRenderer;
import mx.controls.treeClasses.TreeListData;
import mx.events.FlexEvent;


public class ThreeStatusCheckBoxTreeItemRenderer extends TreeItemRenderer
{
private static var _colorForThirdState:int = 0x37BEF8;
private static var _selectedField:String = "selected";
private var checkBox:CheckBox;


public function ThreeStatusCheckBoxTreeItemRenderer()
{
super();
}


override protected function createChildren():void
{
super.createChildren();
checkBox = new CheckBox();
addChild(checkBox);
checkBox.addEventListener(Event.CHANGE, changeHandler);
}


/**//**
* Initial data when component initialization
*
*/

override protected function commitProperties():void
{
super.commitProperties();

if (data && data.@[_selectedField] != null)
{
var s:int = int(data.@[_selectedField]);
var selected:Boolean = s > 0 ? true : false;
checkBox.selected = selected;

} else
{
checkBox.selected = false;
}
}


/**//**
* update dataProvider when user click CheckBox
*
*/

protected function changeHandler(event:Event):void
{

if (data && data.@[_selectedField] != null)
{
data.@[_selectedField] = checkBox.selected ? "1" : "0";
}
var listData:TreeListData = TreeListData(listData);

if (listData.hasChildren)
{
var item:XML = XML(listData.item);
handleAllChildren(item.children());
}
handleAllParents(listData.item.parent());
}


private function handleAllChildren(children:XMLList):void
{

for each (var item:XML in children)
{
item.@[_selectedField] = checkBox.selected ? "1" : "0";
var children:XMLList = item.children();

if (children.length() > 0)
{
handleAllChildren(children);
}
}
}


private function handleAllParents(parent:XML):void
{

if (parent != null)
{
var children:XMLList = parent.children();
var hasSelected1:Boolean = false;
var hasSelected2:Boolean = false;
var hasSelected0:Boolean = false;

for each (var item:XML in children)
{

if (int(item.@[_selectedField]) == 1)
{
hasSelected1 = true;
}

if (int(item.@[_selectedField]) == 2)
{
hasSelected2 = true;
}

if (int(item.@[_selectedField]) == 0)
{
hasSelected0 = true;
}
}

if (checkBox.selected == true)
{

if (!hasSelected0 && !hasSelected2)
{
parent.@[_selectedField] = "1";

} else
{
parent.@[_selectedField] = "2";
}

} else
{

if (!hasSelected1 && !hasSelected2)
{
parent.@[_selectedField] = "0";

} else
{
parent.@[_selectedField] = "2";
}
}
handleAllParents(parent.parent());
}
}


/**//**
* reset itemRenderer's width
*/

override protected function measure():void
{
super.measure();
measuredWidth += checkBox.getExplicitOrMeasuredWidth();
}


/**//**
* re-assign layout for tree, move lable to right
* @param unscaledWidth
* @param unscaledHeight
*/

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);
}
checkBox.move(startx, (unscaledHeight - checkBox.height) / 2);
label.x = startx + checkBox.getExplicitOrMeasuredWidth();

var node:XML = data as XML;

if (int(node.@[_selectedField]) == 2)
{
fillCheckBox(true);

} else
{
fillCheckBox(false);
}

}

/**//**
* re-draw check box for the third state
* @param isFill
*/

private function fillCheckBox(isFill:Boolean):void
{
checkBox.validateNow();
checkBox.graphics.clear();

if (isFill)
{
var myRect:Rectangle = checkBox.getBounds(checkBox);
checkBox.graphics.beginFill(_colorForThirdState, 1);
checkBox.graphics.drawRoundRect(myRect.x, myRect.y, myRect.width, myRect.height, 1, 0x00FF00);
checkBox.graphics.endFill();
}
}


}
}
然后在tree组件中使用这个renderer就可以了。
<?
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/mx
"
minWidth
=
"
955
"
minHeight
=
"
600
"
xmlns:robin
=
"
com.robin.*
"
>
<
fx:Declarations
>
<
fx:XMLList id
=
"
treeData
"
>
<
node name
=
"
ShangHai
"
type
=
"
ROOT
"
selected
=
"
1
"
>
<
node name
=
"
HuangPu
"
type
=
"
NODE
"
selected
=
"
1
"
>
<
node name
=
"
A
"
type
=
"
NODE
"
selected
=
"
1
"
/>
<
node name
=
"
B
"
type
=
"
NODE
"
selected
=
"
1
"
/>
</
node
>
<
node name
=
"
PuDong
"
type
=
"
NODE
"
selected
=
"
1
"
/>
</
node
>
<
node name
=
"
Beijing
"
type
=
"
ROOT
"
selected
=
"
2
"
>
<
node name
=
"
HaiDian
"
type
=
"
NODE
"
selected
=
"
0
"
/>
<
node name
=
"
ChaoYang
"
type
=
"
NODE
"
selected
=
"
1
"
/>
</
node
>
</
fx:XMLList
>
</
fx:Declarations
>
<
fx:Script
>
<!
[CDATA[

protected
function
getCurrentData_clickHandler(event:MouseEvent):
void
{
currentText.text = String(treeData);
}
]]
>
</
fx:Script
>
<
mx:Tree x
=
"
0
"
y
=
"
0
"
width
=
"
232
"
height
=
"
285
"
itemRenderer
=
"
com.robin.ThreeStatusCheckBoxTreeItemRenderer
"
labelField
=
"
@name
"
dataProvider
=
"
{treeData}
"
/>
<
s:Button x
=
"
43
"
y
=
"
293
"
label
=
"
Get Current Data
"
id
=
"
getCurrentData
"
click
=
"
getCurrentData_clickHandler(event)
"
/>
<
s:TextArea x
=
"
253
"
y
=
"
0
"
width
=
"
459
"
height
=
"
285
"
id
=
"
currentText
"
/>
</
s:Application
>

不多说费话了,直接进入主题,看看如何实现。其实在Flex中,只用自己实现一个TreeItemRenderer就可以了,代码如下:







































































































































































































































然后在tree组件中使用这个renderer就可以了。

































