常常会用到带有3种状态CheckBox的树形组件,比如在权限管理中,或者是地区选择中等等,如下图:
不多说费话了,直接进入主题,看看如何实现。其实在Flex中,只用自己实现一个TreeItemRenderer就可以了,代码如下:
然后在tree组件中使用这个renderer就可以了。
就是这么简单!
不多说费话了,直接进入主题,看看如何实现。其实在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();
}
}
}
}
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 >
< 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 >
就是这么简单!