Flex使用ArrayCollection实现动态Tree

接触Flex4一个多月,系统地了解了Flex4主要技术。近日想做一个Tree控件的Demo,但发现关于Flex4的很多书籍中主要描述的数据展现控件为DataGrid,List,几乎不涉及Tree,查阅Flex4官方API,看到了Tree控件的简单Demo,使用Flex4内置的XML数据结构填充Tree的dataProvider属性。数据源如下:
<fx:Declarations>
		<fx:XMLList id="treeData">
			<node label="MailBox">
				<node label="Inbox">
					<node label="Marketing"/>
					<node label="Product Management"/>
					<node label="Personal"/>
				</node>
				<node label="Outbox">
					<node label="Professional"/>
					<node label="Personal"/>
				</node>
				<node label="Spam"/>
				<node label="Sent"/>
			</node>
		</fx:XMLList>
	</fx:Declarations>

在Tree中指定dataProvider属性和labelField属性即可展示树。
<mx:Tree id="myTree" width="50%" height="100%" 
						 dataProvider="{treeData}"
						 labelField="@label"
						 showRoot="true”>
</mx:Tree>

   但是在实际应用中,面对上万甚至上百万条的数据我们不可能使用静态树,更多是使用动态树。从网上查阅了很多,关于动态树的解决方案基本都是基于XML数据源的,即点击节点时调用RemoteService去Java服务端获取XML来展现子节点。这样的问题对于我来说,就是要在后台对数据库获取的数据进行XML格式的封装,前台也要对XML进行操作。方法不当的话,效率会很低下。我们更希望直接使用Java返回的List对象数组进行树的展示。即在前台使用ArrayCollection作为Tree的dataProvider。结合网上的资料,自己使用了如下的实现方案,在此总结一下。

Server端(Java)
使用VO来封装数据库的数据,其中需要有一个类型为List的children属性,由于Tree控件不像DataGrid,List控件,它拥有层次结构。
package com.ls.vo;

import java.util.List;

public class Spec {
	private String id;
	private String name;
	private String type;
	private List<Spec> children;
	
	public Spec(){
		
	}
	public Spec(String id, String name, String type) {
		super();
		this.id = id;
		this.name = name;
		this.type = type;
	}

	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public List<Spec> getChildren() {
		return children;
	}

	public void setChildren(List<Spec> children) {
		this.children = children;
	}
}

构建一个Spec的Service类实现业务操作。简单起见,将Service和Dao融合在了一起。
package com.ls.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.longshine.indigo.dbutils.DBUtils;
import com.ls.vo.Spec;

public class SpecService {

	public List<Spec> getDevMainTypeList() {
		List<Spec> list = new ArrayList<Spec>();
		String sql = "select distinct asset_main_type_id id,asset_main_type_name name from scgl.t_jx_spec";
		ResultSet res = DBUtils.executeQuery(sql, null);
		try {
			while (res.next()) {
				Spec spec = new Spec(res.getString("id"),
				res.getString("name"), "devmaintype");												spec.setChildren(new ArrayList<Spec>());
				list.add(spec);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
	}

	public List<Spec> getSpecListByDevMainTypeID(String id) {
		String sql = "select * from t_jx_spec where asset_main_type_id=?";
		List<Spec> list = new ArrayList<Spec>();
		ResultSet res = DBUtils.executeQuery(sql, new Object[] { id });
		try {
			while (res.next()) {
				Spec spec = new Spec(res.getString("id"),
				res.getString("name"), "spec");
				spec.setChildren(new ArrayList<Spec>());
				list.add(spec);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
	}
	public List<Spec> getPropListBySpecID(String id) {
		String sql = "select * from t_jx_prop where spec_id=?";
		List<Spec> list = new ArrayList<Spec>();
		ResultSet res = DBUtils.executeQuery(sql, new Object[] { id });
		try {
			while (res.next()) {
				Spec spec = new Spec(res.getString("id"),
						res.getString("name"), "prop");
				list.add(spec);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
	}
}

然后在remote-config.xml中设置服务,这样Java端的工作就已经完成。
<destination id="specService">
		<properties>
			<source>com.ls.service.SpecService</source>
		</properties>
	</destination>

Client端(Flex4)
首先在Flex中构建一个与Java端Spec对象相映射的VO类——SpecVO。
package vo
{
	import mx.collections.ArrayCollection;

	[RemoteClass(alias="com.ls.vo.Spec")]
	public class SpecVO
	{
		public var id:String;
		public var name:String;
		public var type:String;
		public var children:ArrayCollection;
		public function SpecVO()
		{
		}
	}
}

接下来就是MXML文件,
<?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" creationComplete="initApp()">
	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.ListEvent;
			import mx.events.TreeEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;
			import vo.SpecVO;
			
			[Bindable]
			[ArrayElementType("vo.SpecVO")]
			private var treeData:ArrayCollection=new ArrayCollection();
			[Bindable]
			private var selectedNode:SpecVO;
		
			protected function initApp():void{
				remoteService.getDevMainTypeList();
			}
			
			protected function remoteService_faultHandler(event:FaultEvent):void
			{
				Alert.show(event.fault.message);
			}
			
			protected function getDevMainTypeList_resultHandler(event:ResultEvent):void
			{
				treeData=event.result as ArrayCollection;
				
			}
			
			protected function getSpecListByDevMainTypeID_resultHandler(event:ResultEvent):void
			{
				selectedNode.children=event.result as ArrayCollection;
				 treeData.itemUpdated(selectedNode);
			}
			
			protected function getPropListBySpecID_resultHandler(event:ResultEvent):void
			{
				selectedNode.children=event.result as ArrayCollection;
				treeData.itemUpdated(selectedNode);
			}
			
			protected function specTree_itemOpeningHandler(event:TreeEvent):void
			{
				selectedNode=event.item as SpecVO;
				if(!specTree.isItemOpen(event.item)&&selectedNode.children.length==0)
				{
					if(selectedNode.type=="devmaintype")
						remoteService.getSpecListByDevMainTypeID(selectedNode.id);
						
					else if(selectedNode.type=='spec')
						remoteService.getPropListBySpecID(selectedNode.id);
				}
			}
		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- 将非可视元素(例如服务、值对象)放在此处 -->
		<s:RemoteObject id="remoteService" destination="specService"
						fault="remoteService_faultHandler(event)">
			<s:method name="getDevMainTypeList" result="getDevMainTypeList_resultHandler(event)"/>
			<s:method name="getSpecListByDevMainTypeID"
					  result="getSpecListByDevMainTypeID_resultHandler(event)"/>
			<s:method name="getPropListBySpecID" result="getPropListBySpecID_resultHandler(event)"/>
		</s:RemoteObject>
	</fx:Declarations>
	<s:Panel width="75%" height="75%" horizontalCenter="0" title="Halo Tree Control Example"
			 verticalCenter="0">
		<s:VGroup left="10" right="10" top="10" bottom="10">
			<s:Label width="100%" color="blue" text="Select a node in the Tree control."/>
			
			<mx:HDividedBox width="100%" height="100%">
				<mx:Tree id="specTree" width="50%" height="100%" dataProvider="{treeData}"
						 itemOpening="specTree_itemOpeningHandler(event)" labelField="name"
						 showRoot="true">
				</mx:Tree>
				<s:TextArea width="50%" height="100%" text="Selected Item:"/>
			</mx:HDividedBox>
		</s:VGroup>
	</s:Panel>
</s:Application>

程序初始化时调用RemoteService来加载第一级节点,点击节点时会首先判断节点是否第一次展开,如果是则根据SpecVO的type字段来选择方法去后台获取子节点。resultHandler中将获取的ArrayCollection赋值给节点的children字段,然后使用ArrayCollection的itemUpdate()方法更新数据源。此方法若没有则Tree无法马上渲染子节点。
启动Tomcat,运行Flex页面测试如下:


Flex使用ArrayCollection实现动态Tree

到此基本实现了Flex的Tree的延迟动态加载,至于之后的增删改就容易多了。希望通过后期的了解和学习能有更合适的解决方案。

你可能感兴趣的:(Flex,tree,arrayCollection,动态加载)