在周末挤出了一点时间,写了一个三态复选框的组件,单独使用没有价值,不过集成到树之中可以很好的实现三态树,今天上午便把三态树组件也完成了,Flex自定义组件基本无所不能,此组件基于最新的Flex4.6(也支持Apache Flex4.10,我目前已经全面升级到4.10,为了和以后保持一致),废话不表,呆毛如下:
1.首先创建一个类TriStateCheckBox,继承至CheckBox,为了实现复选框的三种状态,我需要设置三个公开的静态常量来表示,用0,1,2来分别表示Unchecked,Checked和Indeterminate三种状态:
1 public class TriStateCheckBox extends CheckBox 2 { 3 public static const Unchecked:int = 0; 4 public static const Checked:int = 1; 5 public static const Indeterminate:int = 2; 6 7 public function TriStateCheckBox() 8 { 9 super(); 10 } 11 }
2.为组件自定义一个checkState属性,用来标识复选框的三种状态,这里需要注意的是当状态处于Checked和Indeterminate时,我们认为复选框的selected=true,因此还需要重写复选框的selected属性,实现两者之间的协调统一:
1 private var _checkState:int; 2 [Bindable(event="change")] 3 [Inspectable(category="General", defaultValue="0")] 4 public function get checkState():int 5 { 6 return _checkState; 7 } 8 public function set checkState(value:int):void 9 { 10 if(value == _checkState) 11 return; 12 13 _checkState = value; 14 if(_checkState==Indeterminate || _checkState==Checked) 15 { 16 _selected = true; 17 } 18 else 19 { 20 _selected = false; 21 } 22 dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); 23 invalidateSkinState(); 24 } 25 26 private var _selected:Boolean; 27 [Bindable] 28 [Inspectable(category="General", defaultValue="false")] 29 override public function get selected():Boolean 30 { 31 return _selected; 32 } 33 override public function set selected(value:Boolean):void 34 { 35 if(value == selected && checkState!=Indeterminate) 36 { 37 return; 38 } 39 _selected = value; 40 _checkState = _selected?Checked:Unchecked; 41 dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT)); 42 invalidateSkinState(); 43 }
3.现在来考虑复选框的外观,我们新建一个皮肤,主机组件设为上面建立的TriStateCheckBox,然后我们就能看到CheckBox的外观代码,大概分成了两个组,一种是没有勾选的状态:up,over,down,disabled,一种是已勾选状态:upAndSelected,overAndSelected,downAndSelected,disabledAndSelected,用来呈现两种状态,现在我就需要再额外增加一种状态的呈现,所以加入四种状态即可:upAndIndeterminated,overAndIndeterminated,downAndIndeterminated,disabledAndIndeterminated:
1 <s:states> 2 <s:State name="up" /> 3 <s:State name="over" stateGroups="overStates" /> 4 <s:State name="down" stateGroups="downStates" /> 5 <s:State name="disabled" stateGroups="disabledStates" /> 6 <s:State name="upAndSelected" stateGroups="selectedStates" /> 7 <s:State name="overAndSelected" stateGroups="overStates, selectedStates" /> 8 <s:State name="downAndSelected" stateGroups="downStates, selectedStates" /> 9 <s:State name="disabledAndSelected" stateGroups="disabledStates, selectedStates" /> 10 <s:State name="upAndIndeterminated" stateGroups="indeterminatedStates" /> 11 <s:State name="overAndIndeterminated" stateGroups="overStates, indeterminatedStates" /> 12 <s:State name="downAndIndeterminated" stateGroups="downStates, indeterminatedStates" /> 13 <s:State name="disabledAndIndeterminated" stateGroups="disabledStates, indeterminatedStates" /> 14 </s:states>
4.好了,我现在要重写组件的状态切换逻辑,以便使组件根据三种状态来切换对应的state:
1 override protected function getCurrentSkinState():String 2 { 3 var state:String = super.getCurrentSkinState(); 4 if(checkState==Indeterminate) 5 { 6 switch(state) 7 { 8 case "up": 9 case "upAndSelected": 10 { 11 state = "upAndIndeterminated"; 12 break; 13 } 14 case "over": 15 case "overAndSelected": 16 { 17 state = "overAndIndeterminated"; 18 break; 19 } 20 case "down": 21 case "downAndSelected": 22 { 23 state = "downAndIndeterminated"; 24 break; 25 } 26 case "disabled": 27 case "disabledAndSelected": 28 { 29 state = "disabledAndIndeterminated"; 30 break; 31 } 32 } 33 } 34 return state; 35 }
5.最后,只需要在外观代码中加入我想要的第三种状态的外观即可,我个人还是比较喜欢实心方块的外观,因此我打算直接绘制一个Rect填充,此处是可以自行定义的,比如你喜欢画个空心园来表示第三状态,或者画个灰色的勾,都是可以的,思想没有边界,Flex的强大只有懂的人才能体会:
1 <s:Rect left="2" top="2" right="2" bottom="2" includeIn="indeterminatedStates"> 2 <s:fill> 3 <s:SolidColor id="indeterminatedMarkFill" color="0" alpha="0.8" /> 4 </s:fill> 5 </s:Rect>