简介
1. E3.Tree是E3平台下一个用于构造树型UI(menu,tree,outlookbar等)的的组件,
E3.Tree 特色
部署简单,只需要把相关jar放到WEB-INF/lib目录下即可
构造树,菜单等树型UI的开发模式一致,
提供了API和taglib 2种使用方式,使用简单,功能强大
能够很容易把现有的树型UI集成进来,现在支持的有:xtree, ext tree 和yui menu
功能丰富,现在支持的树有 普通树,radio树 ,checkbox树,动态树等
系统要求
JDK1.4X 或者以上版本
E3.tree 有2种使用方式,一种是直接调用API,另外一种是使用taglib,第一种方式只要求jsp1.2,servlet2.3即可。第2种方式需要jsp2.0 servlet2.4
新增功能
提供了taglib的方式来构造树型UI
升级说明
替换E3-Tree.jar
添加commons-beanutils-core.jar
样例部署
把e3.war 放到Tomcat's webapps 目录下,启动服务器,输入地址http://localhost:8080/e3 进入示例主页. 点级 E3.Tree 连接,即可看到示例程序.
示例组图:
使用
Lib文件清单
文件名 版本 说明
E3-Tree.jar 1.0 E3平台的树
E3-TemplateEngine.ja 1.0 E3平台的模板引擎Adapter
commons-logging.jar 1.04 Apache的commons log,
log4j-1.2.14.jar 1.2.14 Apache的log4j
commons-collections-2.1.1.jar 2.1.1 Apache的collections
velocity-1.4.jar 1.4 Apache的模板引擎
commons-beanutils-core.jar 1.6 Apache的BeanUtils,使用里面的PropertyUtils类.
使用taglib
我们先来看看怎么使用taglib.把下面内容命名为E3Tree.jsp,放到例子web应用目录下去,输入地址http://localhost:8080/e3/E3Tree.jsp 看看效果,如果你看到2棵树,说明程序正常没问题,否则请到e3群(21523645)里面问.
<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="e3" uri="/e3/tree/E3Tree.tld" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv=Content-Type content="text/html; charset=utf-8">
<script>
function showSelectedNode(){
var selectModel= tree.getSelectionModel();
var selectNode = selectModel.getSelectedNode();
alert(selectNode.text + selectNode.id );
}
</script>
</HEAD>
<BODY>
<%
java.util.List datas = new java.util.ArrayList();
java.util.Map data = new java.util.HashMap();
data.put("id","10");
data.put("parentId", null );
data.put("name","总部");
datas.add( data );
java.util.Map data1 = new java.util.HashMap();
data1.put("id","1010");
data1.put("parentId", "10" );
data1.put("name","子公司1");
datas.add( data1 );
java.util.Map data2 = new java.util.HashMap();
data2.put("id","1020");
data2.put("parentId", "10" );
data2.put("name","子公司2");
datas.add( data2 );
pageContext.setAttribute("orgs", datas);
%>
<table>
<tr>
<td>
<c:url var="orgIcon" value="/e3/samples/tree/Org.gif"/>
<c:url var="userIcon" value="/e3/samples/tree/User.gif"/>
<e3:tree var="org" items="orgs" builder="extTree">
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
action="javascript:showSelectedNode()"
/>
</e3:tree>
</td>
<td>
<e3:tree var="org" items="orgs" builder="xTree">
<e3:node id="B${org.id}" parentId="B${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
action="javascript:alert('test')"
/>
</e3:tree>
</td>
</tr>
</BODY>
</HTML>
使用taglib步骤
1. 声明taglib
<%@ taglib prefix="e3" uri="/e3/tree/E3Tree.tld" %>
2. 准备业务数据
java.util.List datas = new java.util.ArrayList();
java.util.Map data = new java.util.HashMap();
data.put("id","10");
data.put("parentId", null );
data.put("name","总部");
datas.add( data );
java.util.Map data2 = new java.util.HashMap();
data2.put("id","1020");
data2.put("parentId", "10" );
data2.put("name","子公司2");
datas.add( data2 );
业务数据可以保存在Map或者普通的JAVABEAN中.业务数据必须包含id,parentId,以及节点名称 信息。注意:并不要求他们的属性名是”id” “parented”,”name”,只需要包含了这些信息即可。Id代表节点主键,parentId代表父亲节点主键, name代表节点标题。 如你的业务对象属性名称是orgId, parentOrgId, orgName都可以.
3. 保存业务数据
pageContext.setAttribute("orgs", datas);
可以保存到(pageContext,request, session或application里)
4. 使用taglib显示树
<e3:tree var="org" items="orgs" builder="extTree">
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
action="javascript:showSelectedNode()"
/>
</e3:tree>
Tree标签属性
属性名称 属性类型 备注
var String 用于保存items元素
items String 是业务数据列表对象的key
builder String 用于构造树的builder对象(builder是什么下面会有介绍),可以选值有
[XTree, XLoadTree, RadioXTree, RadioXLoadTree, CheckXTree, CheckXLoadTree, CompositeXTree, CompositeXLoadTree, ExtTree, ExtLoadTree]
如果这些builder不能满足您的需求,你可以指定一个class,只要指定class实现了WebTreeBuilder接口即可.
comparator java.util.Comparator 排序器,用来对树的节点排序. 节点类型为
net.jcreate.e3.tree.support.WebTreeNode
sortProperty String 排序属性名称,默认是按节点的名称来排序的,如果要使用别的属性排序,则需要设置该值.:如果你的业务对象有排序属性时,则需要指定,如sortProperty=”orgOrder”. 注意:如果设置了comparator属性,那么该值无效.
reverse boolean 是否反向排序,默认false
node标签负责将业务对象转换成树节点对象.node taglib包含的常规属性有
属性名称 属性类型 备注
id String 节点id
parentId String 父亲节点id
name String 节点名称(标题)
icon String 节点图标
openIcon String 节点展开时的图标
action String 是单节点时的动作,可以是一个url也可以是javascript函数.如果是函数,则必须以javascript:开头.如:action=”javascript: alert(‘demo’)”
nodeProperty String 用于设置节点类型,有效值radio,checkbox和none, nodeProperty的默认值是none,表示节点旁边没有其他控件,为radio时,节点旁边会有个单选按纽,为checkbox时,节点旁边会有个checkbox按纽.
selected boolean 是否选种节点,只有当nodeProperty为radio或checkbox时才有效,默认值为false
disabled boolean 是否禁用节点,默认值为false
value String 节点帮定的值,只有当nodeProperty为radio或checkbox时才有效,默认值为空(长度为0的字符串)
dragable boolean 节点是否允许拖动,默认值为false
dropable boolean 是否允许停放拖动的节点,默认值为false
添加JAR到classpath中
现在来看调用API的使用方式.新建一个web项目,把Lib文件清单中的jar全部添加到classpath中
业务数据对象
package net.jcreate.e3.samples.tree;
public class Org {
private String id;
private String parentId;
private String name;
private int viewOrder;
public Org(){
}
public Org(String pId, String pParentId, String pName, int pViewOrder){
this.id = pId;
this.parentId = pParentId;
this.name = pName;
this.viewOrder = pViewOrder;
}
public int getViewOrder() {
return viewOrder;
}
public void setViewOrder(int viewOrder) {
this.viewOrder = viewOrder;
}
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 getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
}
控制器Servlet
package net.jcreate.e3.samples.tree;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.jcreate.e3.tree.Node;
import net.jcreate.e3.tree.TreeDirector;
import net.jcreate.e3.tree.TreeModel;
import net.jcreate.e3.tree.UncodeException;
import net.jcreate.e3.tree.UserDataUncoder;
import net.jcreate.e3.tree.support.AbstractWebTreeModelCreator;
import net.jcreate.e3.tree.support.DefaultTreeDirector;
import net.jcreate.e3.tree.support.WebTreeBuilder;
import net.jcreate.e3.tree.support.WebTreeNode;
import net.jcreate.e3.tree.xtree.XTreeBuilder;
public class TestServlet extends HttpServlet{
protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
//业务数据
List orgs = new ArrayList();
Org jcjtOrg = new Org("001",null,"进创集团", 1);
Org jcrjOrg = new Org("001001","001","进创软件", 1);
Org xrjOrg = new Org("0010010011","001001","X软件公司", 1);
Org yrjOrg = new Org("0010010012","001001","Y软件公司", 2);
Org zrjOrg = new Org("0010010013","001001","Z软件公司", 3);
orgs.add(jcjtOrg);
orgs.add(jcrjOrg);
orgs.add(xrjOrg);
orgs.add(yrjOrg);
orgs.add(zrjOrg);
//业务数据解码器,从业务数据中分解出id和parentid
UserDataUncoder orgUncoder = new UserDataUncoder(){
public Object getID(Object pUserData) throws UncodeException {
Org org = (Org)pUserData;
return org.getId();
}
public Object getParentID(Object pUserData) throws UncodeException {
Org org = (Org)pUserData;
return org.getParentId();
}
};
//Tree模型构造器,用于生成树模型
AbstractWebTreeModelCreator treeModelCreator =
new AbstractWebTreeModelCreator(){
//该方法负责将业务数据映射到树型节点
protected Node createNode(Object pUserData, UserDataUncoder pUncoder) {
Org org = (Org)pUserData;
WebTreeNode result = new WebTreeNode(org.getName(), "org" + org.getId());
//action是点击按纽执行的方法.可以是url,或者javascript函数
result.setAction("javascript:alert(' " + org.getName() + "')");
return result;
}
};
treeModelCreator.init(pRequest);
TreeModel treeModel = treeModelCreator.create(orgs,orgUncoder);
TreeDirector director = new DefaultTreeDirector();//构造树导向器
WebTreeBuilder treeBuilder = new XTreeBuilder();//构造树Builder
treeBuilder.init(pRequest);
director.build(treeModel, treeBuilder);//执行构造
String treeScript = treeBuilder.getTreeScript();//获取构造树的脚本
pRequest.setAttribute("treeScript", treeScript);//保存到request,以便页面使用
pRequest.getRequestDispatcher("XTree.jsp").forward(pRequest,pResponse);
}
}
上面代码构造是普通树,如果要构造带checkbox/radiobox的树,只需要将
WebTreeBuilder treeBuilder = new XTreeBuilder()
这行代码换成
WebTreeBuilder treeBuilder = new CheckXTreeBuilder ()
或
WebTreeBuilder treeBuilder = new RadioXTreeBuilder ()
即可
JSP页面
命名为XTree.jsp,放在web应用跟目录下(WEB-INF所在目录);
%@ page contentType="text/html; charset=utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv=Content-Type content="text/html; charset=utf-8">
</HEAD>
<BODY>
<%= request.getAttribute("treeScript") %>
</BODY>
</HTML>
web.xml配置
把下面配置添加到web.xml文件
<listener> <listener-class>net.jcreate.e3.tree.loader.LoadResourcesListener</listener-class>
</listener>
<filter>
<filter-name>e3/characterEncodingFilter</filter-name>
<filter-class>net.jcreate.e3.web.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>e3/characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>net.jcreate.e3.samples.tree.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/testServlet</url-pattern>
</servlet-mapping>
测试
将web项目部署到Tomcat's webapps的test目录
输入地址 http://localhost:8080/test/testServlet 如果看到树,恭喜你成功了!
体系结构
db
xml
ldap
mem
灰色大框图里的部件是E3.Tree的,框外是E3.Tree需要的输入和输出
业务数据的来源会很多,常见的有db,xml,ldap和内存,所以对于数据如何获取,在E3.Tree里不做定义
业务数据以Collection的形式提交给E3.Tree
解码器:负责业务对象解码,分解出ID和parentID
模型构造器:使用解码器对业务数据进行处理,生成树模型
树模型:就是树的内存结构,可以有一个或者多个跟节点
树构造器:负责对树模型中的节点进行处理,将内存节点翻译成用于与构造特定树型UI的脚本.
导向器:负责树模型的节点遍历,驱动树构造器的处理.
排序器:负责节点的排序
访问器:负责节点过滤或者节点遍历.
设计模型
E3.Tree采用的核心设计模式是builder模式,如果您对builder还不大清楚,可以找gof设计模式树翻翻
业务对象
具有树型属性(构造树型结构需要的属性)的业务数据对象,从这类对象中可以分解出ID,和parentID.这类业务对象通常是会下面几种结构中的一种.
如:
A{
Private String id;
Private String parentId ;
}
B{
Private String ID; //ID的值是 0000_0001, 0000_0001_0001这种形式
}
E3.Tree对业务数据的唯一要求就是:从业务对象中要能分解出ID和父亲ID。ID和parentID可以是任何数据类型
业务数据集
业务对象的集合,该集合里的业务对象是线型关系。彼此之间没关系.
由于业务数据的来源会很多,常见的有db,xml,ldap和内存,所以对于数据如何获取,在E3.Tree里不做定义.E3.Tree只要求以Collection的形式传递给TreeModelCreator即可.
节点Node
跟数据结构里的节点概念一致,包含的方法有
public interface Node {
//获取父亲节点
public Node getParent();
//设置父亲节点
public void setParent(Node pParent);
//设置业务对象
public void setUserData(Object pUserData);
//获取业务对象
public Object getUserData();
//获取所有儿子
public Iterator getChildren();
//删除儿子
public void detachNode(Node pNode);
//添加儿子
public void addNode(Node pNode);
//是否是叶子节点
public boolean isLeaf();
//是否是根节点
public boolean isRoot();
//儿子节点个数
public int getChildCount();
//获取指定位置节点,序号从0开始
public Node getChildAt(int pChildIndex);
//获取儿子节点的序号
public int getIndex(Node node);
}
树模型TreeModel
TreeModel 跟数据结构里的树的概念对应. TreeModel可以有一个跟节点也可以有多个跟节点,所以可以是树有可以是森林.
/**
* 树,森林
* @author new
*
*/
public interface TreeModel {
/**
* 获取跟节点,可以是多个跟节点.
* @return
*/
public Iterator getRootNodes();
}
解码器UserDataUncoder
负责业务对象解码,分解出ID和parentID. 如果分解过程出错,抛UncodeException或它的派生类异常.TreeModelCreator会使用它对业务对象进行解码(我把从业务对象中分解出ID和parentID的过程叫做解码)处理.
/**
* 负责业务对象解码,分解出ID和parentID
* @author new
*
*/
public interface UserDataUncoder {
public Object getID(Object pUserData) throws UncodeException;
public Object getParentID(Object pUserData) throws UncodeException;
}
树模型构造器TreeModelCreator
负责创建TreeModel对象.注意,存在2个create方法,请看注释,理解2种方法的区别.
/**
* 将业务数据构造成TreeModel
* @author new
*
*/
public interface TreeModelCreator {
/**
* 创建树模型
* @param pUserDatas 业务数据,至少要存在一个跟节点(不存在父亲节点的节点)
* 要求集合元素读必须实现Uncodable接口
* @return 返回根节点.
* @throws CreateTreeModelException 如果集合元素没有实现Uncodable接口,会抛出
* ClassCastException异常
*/
public TreeModel create(Collection pUserDatas) throws CreateTreeModelException;
/**
* 创建树模型
* @param pUserDatas 业务数据,至少要存在一个跟节点(不存在父亲节点的节点)
* @param pUncoder 解码器,对每个业务数据进行解码,返回主键对象和父亲主键对象.
* @return 返回根节点.
* @throws CreateTreeModelException
*/
public TreeModel create(Collection pUserDatas, UserDataUncoder pUncoder) throws CreateTreeModelException;
}
树构造器TreeBuilder
负责对树模型中的节点进行处理,将内存节点翻译成用于与构造特定树型UI的脚本.
TreeBuilder包含一系列树型UI构造过程方法。请看注释,理解各方法的含义.
public interface TreeBuilder {
/**
* 开始构造树
* @throws BuildTreeException
*/
public void buildTreeStart() throws BuildTreeException;
/**
* 结束构造树
* @throws BuildTreeException
*/
public void buildTreeEnd() throws BuildTreeException;
/**
* 开始构造普通节点(除跟节点之外的节点)
* @param pNode 当前节点
* @param pParentNode 父亲节点
* @param pLevel 节点级别,根节点为0级,根节点直接儿子节点为1级,依次类推,2,3,....
* @param pRow 在兄弟节点里的序号,第一个兄弟节点为0,第2个为1,第3个为2,依次类推.3,4....
* @throws BuildTreeException
*/
public void buildNodeStart(Node pNode, Node pParentNode, int pLevel, int pRow)
throws BuildTreeException;
/**
* 结束构造普通节点(除跟节点之外的节点)
* @param pNode 当前节点
* @param pParentNode 父亲节点
* @param pLevel 节点级别,根节点为0级,根节点直接儿子节点为1级,依次类推,2,3,....
* @param pRow 在兄弟节点里的序号,第一个兄弟节点为0,第2个为1,第3个为2,依次类推.3,4....
* @throws BuildTreeException
*/
public void buildNodeEnd(Node pNode, Node pParentNode, int pLevel, int pRow)
throws BuildTreeException;
/**
* 开始构造跟节点
* @param pRootNode 跟节点,非空
* @param pLevel 根节点级别
* @param pRow 在兄弟节点里的序号,第一个兄弟节点为0,第2个为1,第3个为2,依次类推.3,4....
* @throws BuildTreeException
*/
public void buildRootNodeStart(Node pRootNode,int pLevel, int pRow) throws BuildTreeException;
/**
* 结束构造跟节点
* @param pRootNode 跟节点,非空
* @param pLevel 根节点级别
* @param pRow 在兄弟节点里的序号,第一个兄弟节点为0,第2个为1,第3个为2,依次类推.3,4....
* @throws BuildTreeException
*/
public void buildRootNodeEnd(Node pRootNode, int pLevel, int pRow) throws BuildTreeException;
}
导向器TreeDirector
负责树模型的节点遍历,驱动树构造器的处理.
public interface TreeDirector {
/**
* 设置节点比较器
* @param pComparator 节点比较器,用于进行兄弟节点比较
*/
public void setComparator(Comparator pComparator);
/**
* 节点访问者
* @param pVisitor
*/
public void setNodeVisitor(NodeVisitor pVisitor);
/**
* build树
* @param pTree
* @param pTreeBuilder Tree构造器(非空)
* @throws BuildTreeException
*/
public void build(TreeModel pTree, TreeBuilder pTreeBuilder) throws BuildTreeException;
}
排序器Comparator
负责节点的排序处理.排序器是使用jdk自带的java.util.Comparator
访问器NodeVisitor
负责节点过滤或者节点遍历.
/**
* 在使用TreeBuilder构造节点前进行访问.可以通过NodeVisitor
* 设置Node的属性,过滤节点.当访问一个节点返回false时,
* 该节点和他所有儿子节点不会传递个TreeBuilder
* 节点访问.
* @author 黄云辉
*
*/
public interface NodeVisitor {
public boolean visit(Node pNode);
}
API代码片段
排序
节点默认是不排序,如果要排序,需要给导向器TreeDirector设置排序器.
net.jcreate.e3.tree.support.DefaultNodeComparator是E3.Tree内置的排序器,根据节点名称排序.
TreeDirector director = new DefaultTreeDirector();//构造树导向器
director.setComparator(new DefaultNodeComparator());
如果要根据特定属性排序,则需要自己实现排序器,通常只需要从
net.jcreate.e3.tree.support.AbstractNodeComparator派生即可
TreeDirector director = new DefaultTreeDirector();//构造树导向器
director.setComparator(new AbstractNodeComparator(){
protected Comparable getComparableProperty(Node pNode) {
Object userData = pNode.getUserData();//获取业务对象
Org org = (Org)userData;
return new Integer( org.getViewOrder() );
}
});
设置节点图标
设置节点图标
//Tree模型构造器,用于生成树模型
AbstractWebTreeModelCreator treeModelCreator =
new AbstractWebTreeModelCreator(){
//该方法负责将业务数据映射到树型节点
protected Node createNode(Object pUserData, UserDataUncoder pUncoder) {
Org org = (Org)pUserData;
WebTreeNode result = new WebTreeNode(org.getName(), "org" + org.getId());
result.setIcon(this.getUrl("/e3/samples/tree/Org.gif"));
result.setOpenIcon(this.getUrl("/e3/samples/tree/Org.gif"));
//action是点击按纽执行的方法.可以是url,或者javascript函数
result.setAction("javascript:alert(' " + org.getName() + "')");
return result;
}
};
类 net.jcreate.e3.tree.support. DefaultNode
属性 名称 备注
name 节点名称
类 net.jcreate.e3.tree.support.WebTreeNode
属性 名称 备注
id 节点ID 注意:该ID要是合法标识符号,因为TreeBuilder可能使用该ID作为js变量
icon 节点图标 收缩时图标
openIcon 打开时的图标 展开时图标
action 动作 单击节点时的动作,可以是javascript,也可以是超级连接 如: javascript:orgClick();
/e3/a.jsp
selected 是否被选种
disabled 是否被禁用
nodeProperty 节点属性 目前只有3种属性radio,checkbox和none
这个属性需特别说明:一个树的最终形态是由TreeBuilder来决定的。所以如果采用CheckXTreeBuilder或RadioXTreeBuilder构造树时 ,该属性是没有用的。只有当一棵树中既有check节点又有radio节点或普通节点时,该属性才有效.
类 net.jcreate.e3.tree.support. WebTreeDynamicNode
属性 名称 备注
subTreeURL 子树URL 负责导入子树的URL
节点过滤
过滤掉所有没有儿子节点的跟节点.返回false的节点以及他的儿子节点都会被过滤掉
director.setNodeVisitor(new NodeVisitor(){
public boolean visit(Node pNode) {
boolean noChildRoot = pNode.isRoot() && (pNode.getChildCount() == 0);
if (noChildRoot).{
return false;
} else {
return true;
}
}
});
设置所有叶子节点图标
director.setNodeVisitor(new NodeVisitor(){
public boolean visit(Node pNode) {
if (pNode.isLeaf() ){
result.setIcon(RequestUtil.getUrl("/e3/samples/tree/Org.gif", pRequest));
result.setOpenIcon(RequestUtil.getUrl("/e3/samples/tree/Org.gif", pRequest));
}
return true;
}
});
说明:RequestUtil的包名是net.jcreate.e3.tree.support
pRequest是HttpServletRequest对象,因为是在匿名类里使用,所以pRequest必须是final 类型的.否则编译不能通过.
构造混合节点树
这棵树有机构节点和用户节点,用户节点挂在机构下.用户有个属性指向所属机构.
混合节点的构造跟单节点的构造基本相同。就有一点要特别注意:因为是多节点,可能存在
主键冲突的问题,所以构造节点ID和做节点分解的时候,需要带上前缀.
public void showMixTree(final HttpServletRequest pRequest,
final HttpServletResponse pResponse) throws Exception{
//业务数据
List orgs = new ArrayList();
Org jcjtOrg = new Org("001",null,"进创集团", 1);
Org jcrjOrg = new Org("001001","001","进创软件", 1);
orgs.add(jcjtOrg);
orgs.add(jcrjOrg);
User huangy = new User("huangyh", "黄云辉", "001");//直属集团
User guohp = new User("guohp", "郭鸿鹏", "001");//直属集团
User caogp = new User("caogp", "曹高平", "001001");//进创软件
List users = new ArrayList();
users.add(huangy);
users.add(guohp);
users.add(caogp);
List allData = new ArrayList();
allData.addAll(orgs);
allData.addAll(users);
//业务数据解码器,从业务数据中分解出id和parentid
UserDataUncoder uncoder = new UserDataUncoder(){
final String USERID_PREFIX = "USER_";//为了避免用户ID和机构ID出现相同的情况,所以构造树时
//所有用户ID带个前缀.
public Object getID(Object pUserData) throws UncodeException {
if ( pUserData instanceof Org){
Org org = (Org)pUserData;
return org.getId();
}
if ( pUserData instanceof User ){
User user = (User)pUserData;
return USERID_PREFIX + user.getId();
}
throw new UncodeException("不支持的数据对象." + pUserData.getClass().getName());
}
public Object getParentID(Object pUserData) throws UncodeException {
if ( pUserData instanceof Org){
Org org = (Org)pUserData;
return org.getParentId();
}
if ( pUserData instanceof User ){
User user = (User)pUserData;
return user.getOrgId();
}
throw new UncodeException("不支持的数据对象." + pUserData.getClass().getName());
}
};
//Tree模型构造器,用于生成树模型
AbstractWebTreeModelCreator treeModelCreator =
new AbstractWebTreeModelCreator(){
//该方法负责将业务数据映射到树型节点
protected Node createNode(Object pUserData, UserDataUncoder pUncoder) {
if ( pUserData instanceof Org ){
Org org = (Org)pUserData;
WebTreeNode result = new WebTreeNode(org.getName(), "org" + org.getId());
result.setIcon(this.getUrl("/e3/samples/tree/Org.gif"));
result.setOpenIcon(this.getUrl("/e3/samples/tree/Org.gif"));
//action是点击按纽执行的方法.可以是url,或者javascript函数
result.setAction("javascript:alert(' " + org.getName() + "')");
return result;
}
if ( pUserData instanceof User ){
User user = (User)pUserData;
WebTreeNode result = new WebTreeNode(user.getName(), "user" + user.getId());
result.setIcon(this.getUrl("/e3/samples/tree/User.gif"));
result.setOpenIcon(this.getUrl("/e3/samples/tree/User.gif"));
//action是点击按纽执行的方法.可以是url,或者javascript函数
result.setAction("javascript:alert(' " + user.getName() + "')");
return result;
}
throw new UncodeException("不支持的数据对象." + pUserData.getClass().getName());
}
};
treeModelCreator.init(pRequest);
TreeModel treeModel = treeModelCreator.create(allData,uncoder);
TreeDirector director = new DefaultTreeDirector();//构造树导向器
WebTreeBuilder treeBuilder = new XTreeBuilder();//构造树Builder
treeBuilder.init(pRequest);
director.build(treeModel, treeBuilder);//执行构造
String treeScript = treeBuilder.getTreeScript();//获取构造树的脚本
pRequest.setAttribute("treeScript", treeScript);//保存到request,以便页面使用
pRequest.getRequestDispatcher("/e3/samples/tree/XTree.jsp").forward(pRequest,pResponse);
}
构造动态树
动态树的构造分2个过程,第一个过程构造顶层静态节点(第一次要显示的节点),第2个过程负责导入特定节点的儿子节点数据.
通常第一个过程只显示跟节点,下面的代码
public void showLoadTree(final HttpServletRequest pRequest,
final HttpServletResponse pResponse) throws Exception{
WebTreeDynamicNode rootNode = new WebTreeDynamicNode("进创集团", "org" + "001");
rootNode.setSubTreeURL(
RequestUtil.getUrl("/servlet/xtreeServlet?_actionType=" +
"loadSubOrgs&parentID=" + "001", pRequest));
DefaultTreeModel treeModel = new DefaultTreeModel();
treeModel.addRootNode(rootNode);
TreeDirector director = new DefaultTreeDirector();
director.setComparator(new DefaultNodeComparator());
WebTreeBuilder treeBuilder = new XLoadTreeBuilder();
treeBuilder.init(pRequest);
director.build(treeModel, treeBuilder);
String treeScript = treeBuilder.getTreeScript();
pRequest.setAttribute("treeScript", treeScript);
pRequest.getRequestDispatcher("/e3/samples/tree/XTree.jsp").forward(pRequest,pResponse);
}
说明:
1. 动态节点对象必须是WebTreeDynamicNode
2. setSubTreeURL方法用于设置导入儿子节点数据的url
下面这个方法用于生成儿子节点XML
public void loadSubOrgs(final HttpServletRequest pRequest,
final HttpServletResponse pResponse) throws Exception{
final String parentID = pRequest.getParameter("parentID");
TreeService treeService = TreeBeanFactory.getTreeService();
List subOrgs = treeService.getSubOrgs(parentID);
UserDataUncoder orgUncoder = new OrgUncoder();
AbstractWebTreeModelCreator treeModelCreator =
new AbstractWebTreeModelCreator(){
protected Node createNode(Object pUserData, UserDataUncoder pUncoder) {
Org org = (Org)pUserData;
WebTreeDynamicNode result = new WebTreeDynamicNode(org.getName(), "org" +org.getId());
result.setSubTreeURL(
getUrl("/servlet/xtreeServlet?_actionType=" +
"loadSubOrgs&parentID=" + org.getId()));
return result;
}
};
treeModelCreator.init(pRequest);
TreeModel treeModel = treeModelCreator.create(subOrgs,orgUncoder);
TreeDirector director = new DefaultTreeDirector();
director.setComparator(new DefaultNodeComparator());
WebTreeBuilder treeBuilder = new XLoadSubTreeBuilder();
treeBuilder.init(pRequest);
director.build(treeModel, treeBuilder);
String treeScript = treeBuilder.getTreeScript();
pResponse.setBufferSize(1024*10);
pResponse.setContentType("text/xml;charset=utf-8");
pResponse.getWriter().write(treeScript);
pResponse.flushBuffer();
return;
}
自定义TreeBuilder
Todo
Taglib使用代码片段
简单树
<e3:tree var="org" items="orgs" >
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"/>
</e3:tree>
设置节点图标
<c:url var="orgIcon" value="/e3/samples/tree/Org.gif"/>
<c:url var="userIcon" value="/e3/samples/tree/User.gif"/>
<e3:tree var="org" items="orgs" >
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
/>
</e3:tree>
动态树
<c:url var="orgIcon" value="/e3/samples/tree/Org.gif"/>
<c:url var="userIcon" value="/e3/samples/tree/User.gif"/>
<c:url var="subTree" value="/servlet/xtreeServlet?_actionType=loadExtSubOrgs&parentID=001"/>
<e3:tree var="org" items="orgs" builder="ExtLoadTree" >
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
subTreeURL="${subTree}"
cls="dynamic"
/>
</e3:tree>
节点排序
<c:url var="orgIcon" value="/e3/samples/tree/Org.gif"/>
<c:url var="userIcon" value="/e3/samples/tree/User.gif"/>
<e3:tree var="org" items="orgs" sortProperty="viewOrder" >
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
/>
</e3:tree>
反向排序
<c:url var="orgIcon" value="/e3/samples/tree/Org.gif"/>
<c:url var="userIcon" value="/e3/samples/tree/User.gif"/>
<e3:tree var="org" items="orgs" sortProperty="viewOrder" reverse="true">
<e3:node id="${org.id}" parentId="${org.parentId}" name="${org.name}"
icon="${orgIcon}"
openIcon="${userIcon}"
/>
</e3:tree>
FAQ
1. 我用的web框架是struts/jsf/webwork/,可以用E3.Tree吗?
E3.Tree对Web 框架只有一个要求,就是能取到HttpServletRequest对象,只要满足这个要求,都可以使用.显然上面这几个框架都可以使用E3.Tree.