在技术上,这其实没什么纠结的,只是上一个功能关于动态grid弄好已经接近18:00,这临近下班的时间再搞这个问题,而且紧急,在感情还是上蛮纠结的。加上系统本来已经就有个tree在运转,当然数据格式不是基于XML的,只不过那个tree实在太臃肿而且问题不断,现在要重写就更纠结了。
还好我之前在另一个模块写过一个基于XML数据的Tree,代码还算简洁,复用度蛮高,只不过那个场景下不是Lazy Loading,现在要改成Lazy Loading而已。连写带调半个小时搞定,呵呵。
首先,上次树的定义可以复用,ABCTree.mxml(为避免×××嫌疑,名字以ABC代替):
<?xml version="1.0" encoding="utf-8"?> <mx:Tree xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Metadata> [Event(name="treeLoaded", type="flash.events.Event")] </mx:Metadata> </mx:Tree>
其次,上次的TreeRadioItemRenderer类也可以复用,只需继承重写两个方法即可满足当前不同业务。TreeRadioItemRenderer.as如下:
public class TreeRadioItemRenderer extends TreeItemRenderer { public function TreeRadioItemRenderer(){ super(); } protected var radioButton:RadioButton; override protected function createChildren():void{ super.createChildren(); radioButton = new RadioButton(); radioButton.selected = false; addChild( radioButton ); radioButton.addEventListener(Event.CHANGE, changeHandler); } /** * update dataProvider when user click CheckBox */ protected function changeHandler( event:Event ):void { //Set Value Here for your Model } /** * Initial data when component initialization */ override protected function commitProperties():void{ super.commitProperties(); if (null != data) { var s:int = int(data.selected); var selected:Boolean = s > 0 ? true : false; radioButton.selected = selected; } else { radioButton.selected = false; } } /** * reset itemRenderer's width */ override protected function measure():void{ super.measure(); measuredWidth += radioButton.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); } radioButton.move(startx, ( unscaledHeight - radioButton.height ) / 2 ); label.x = startx + radioButton.getExplicitOrMeasuredWidth(); } }
继承后的ABCTreeRenderer.as:
public class ABCTreeRenderer extends TreeRadioItemRenderer { public function ABCTreeRenderer() { super(); } public var itemXml: XML; //用来记录选择的Node的值 override protected function changeHandler( event: Event ):void { ABCTree.selectedXmlNode = itemXml; } override public function set data(value:Object):void{ if(value != null){ super.data = value; this.itemXml = XML(value); if(this.itemXml.@id == "1000" ){ super.radioButton.visible = false; }else super.radioButton.visible = true; } } override protected function commitProperties():void { super.commitProperties(); if(ABCTree.selectedXmlNode != null) { if(ABCTree.selectedXmlNode.@id == data.@id) { radioButton.selected = true; }else { radioButton.selected = false; } } } }
这样我可以完整的实现树了:
<scenario:ABCTree id="proTree" dataProvider="{model.abcTreeData}" itemRenderer="...ABCTreeRenderer" itemOpen="onItemOpen(event)" labelField = "@name" treeLoaded="afterInit()" width="100%" dataTipFunction="tipFun" showDataTips="true"/>
页面初始化方法:
private function init():void{
needLoad = false;
model.abcTreeData = XML("<node id=\"1000\" name=\"这是根节点\" level=\"1\" isBranch=\"true\" />");
proTree.dispatchEvent(new Event("treeLoaded"));
}
public function initLoad():void{
if(needLoad)
init();
}
树加载完以后,利用afterInit事件加载第一层数据:
private function afterInit():void{ Mask.show("loadABCTree", "Loading tree data..."); var deg:BusDelegate = new BusDelegate(); deg.getABCTreeNode(model.abcTreeData.@id,classificationsLoaded, loadFailed); } //当load完第一层触发 private function classificationsLoaded(event:ResultEvent):void{ try{ var str:String = exceptionProcess(String(event.result)); if(str != "error"){ var ch:XMLList = new XMLList(str); model.abcTreeData.appendChild(ch); } }catch(e){ needLoad = true; }finally{ Mask.close("loadABCTree"); } } //Load Tree Data 有异常 private function loadFailed(event:FaultEvent):void { needLoad = true; Mask.close("loadABCTree"); if(event.fault.rootCause is ChannelEvent && event.fault.faultString == 'Channel disconnected'){ //channel will handle this fault }else if(event.fault.rootCause is ChannelFaultEvent && event.fault.faultString == 'error'){ channel will handle this fault }else{ if(event.fault.faultString != null && event.fault.faultString.indexOf("RemoteAccessException") >= 0) Alert.show("Exception occurred, may ABC remote service not ready."); else Alert.show("Exception:" + event.fault.faultString); } } //处理后台返回回来是exception数据还是正常的数据,空也算exception private function exceptionProcess(str:String): String{ var respObj:Object = JSON.decode(str); var respStatus: String = respObj.respStatus; if (respStatus == "failed") { var errorMessages: String = respObj.errorMessages; if(errorMessages.lastIndexOf(":")!=-1) errorMessages=errorMessages.substring(errorMessages.lastIndexOf(":")+1,errorMessages.length); Alert.show(errorMessages); return "error"; } return String(respObj.result); }
加载下一层:
private function loadNextLevel():void{
if( theClickItem != null && theClickItem.children().length() == 0 ){
Mask.show("loadABCTree", "Loading tree data...");
var deg:BusDelegate = new BusDelegate ();
deg.getAbcTreeNode(theClickItem.@id,nextLeavlLoaded, loadFailed);
}
}
private function nextLeavlLoaded(event:ResultEvent):void{
try{
var str:String = exceptionProcess(String(event.result));
if(str != "error"){
var ch:XMLList = new XMLList(str);
theClickItem.appendChild(ch);
}
}catch(e){
needLoad = true;
}finally{
Mask.close("loadABCTree");
}
}
最后在onItemOpen(event)事件中来触发loadNextLevel方法:
var theClickItem:XML = null;
private function onItemOpen(event:TreeEvent):void{ theClickItem = XML(event.item); if( theClickItem.@level == 8 )//值需加载8层 return; loadNextLevel(); }