为了让ligerui能更好的工作,我们需要对ligerui的一些常用组件进行封装。首先我们就对最基本的菜单组件进行封装。下面我们先来观察下ligerui中树组件。
从上面的图片中,我们可以清楚的看到,这就是一棵树。树是一种常用的数据结构,下面引用百度百科的语句:
树的递归定义:
树(Tree)是n(n≥0)个结点的有限集T,T为空时称为空树,否则它满足如下两个条件:
(1)有且仅有一个特定的称为根(Root)的结点;
(2)其余的结点可分为m(m≥0)个互不相交的子集Tl,T2,…,Tm,其中每个子集本身又是一棵树,并称其为根的子树(Subree)
下面我们使用接口来描述一颗树。在java中,接口可以很好的描述现实世界中的某种物体具备的行为特点,所以接口非常适合。下面是具体代码:
package net.itaem.ligerui;
import java.util.List;
/**
* liger ui中菜单模型
* 这个接口主要用来描述菜单的一些相关操作
* @date 2014-08-19 10:17 am
* @author 骆宏
* @email 846705189@qq.com
*
* */
public interface ITreeModel {
/**
* 定义一个字符串,用来表示树模型中的菜单节点
* */
String MENU = "menu";
/**
* 定义一个字符串,用来表示数模型中的叶子节点
* */
String LEAF = "leaf";
/**
* 返回当前菜单的结构层次
* @return 返回菜单的结构层次
* 如果是叶子节点,那么返回0
* */
public int level();
/**
* 返回当前节点的所有子节点,子节点包括了子菜单以及叶子节点
* @return 返回当前菜单的全部子节点
* */
public List<ITreeModel> children();
/**
* 返回当前节点的父节点
* @return 返回当前节点的父亲节点,如果没有父亲节点,返回null
* */
public ITreeModel parent();
/**
* 返回当前节点的节点类型,这里的节点类型一共有两种,一种是菜单,另外一种是叶子节点
* @return 节点类型
* @see ITreeModel#MENU
* @see ITreeModel#LEAF
* */
public String nodeType();
/**
* 返回当前节点的url
* @return 当前节点的url
* */
public String url();
/**
* 放回当前节点的id
* @return 当前节点id
* */
public String id();
/**
* 返回节点的名字
* @return 节点名字
* */
public String name();
/**
* 当前节点如果是菜单,那么该菜单默认是否展开呢?如果是返回true,代表展开;否则,代表不展开
* @return 返回菜单节点的展开状态
* */
public boolean isexpand();
/**
* 设置菜单名字
* @param name
* */
public void setName(String name);
/**
* 设置菜单url
* @param url
* */
public void setUrl(String url);
/**
* 设置菜单展开状态
* @param isexpend
* */
public void setIsexpand(boolean isexpand);
/**
* 设置父节点
* @param parent
* */
public void setParent(ITreeModel parent);
/**
* 设置孩子节点
* @param children
* */
public void setChildren(List<ITreeModel> children);
/**
* 设置节点id
* @param id
* */
public void setId(String id);
/**
* 返回该节点的json数据,包含该节点下面的所有子节点
* @return 返回当前节点的json数据
* */
public String toTreeJson();
/**
* 返回从根节点到当前节点的所有节点集合,包含当前节点
* @return 返回根节点到当前节点的集合
* */
public List<ITreeModel> route();
/**
* 返回以当前节点为根节点的一棵子树
* @return 返回以当前节点子根节点的子树
* */
public List<ITreeModel> subTree();
}
在上面我们只是定义了一个树模型。并没有实现代码,为什么这样子做呢?因为每个框架使用的数据格式都不一样,所以我们流出扩展性。如果我们不适用ligerui,而使用dwz,我们的接口类不需要发生变化,只需要添加一个dwz树模型子类即可,ligerui相关组件的代码就无需修改了。 下面使用ligerui实现一个菜单树。具体代码:
package net.itaem.ligerui;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.itaem.vo.MenuVo;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/**
* 这里是liger ui树的插件实现类
* @author 骆宏
* @date 2014-08-19 19:39 am
* @email 846705189@qq.com
* */
public class LigerUiTree implements ITreeModel{
//定义一个level,用来保存树的层次
private int level = 0;
//定义一个url,用来保存当前节点的url
private String url;
//定义一个id,用来保存当前节点id
private String id;
//定义一个isexpend,用来保存节点展开状态
private boolean isexpand;
//定义一个name,用来保存节点的名称
private String name;
//定义一个parent,用来保存节点的父亲节点
private ITreeModel parent;
//定义一个children,用来保存当前节点的所有子节点
private List<ITreeModel> children;
public LigerUiTree(){
}
/**
* 定义一个基本的构造方法,该构造方法的参数都不能为空
* @param id 节点id
* @param name 节点name
* @param url 节点url
* */
public LigerUiTree(String id, String name, String url){
if(id == null || name == null || url == null) throw new RuntimeException("id name url都不能为空");
this.id = id;
this.name = name;
this.url = url;
}
public LigerUiTree(String id, String name, String url, ITreeModel parent) {
this(id, name, url);
this.parent = parent;
}
public LigerUiTree(String id, String name, String url, List<ITreeModel> children) {
this(id, name, url);
this.children = children;
}
@Override
public void setUrl(String url) {
this.url = url;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public void setIsexpand(boolean isexpand) {
this.isexpand = isexpand;
}
@Override
public void setName(String name) {
this.name = name;
}
public void setParent(ITreeModel parent) {
this.parent = parent;
}
/**
* 这里同时会计算树的层次
* 并且这里会同时维护parant - children之间的关联管理,也就是在设置当前节点的子节点时,同时会指点这些子节点的父亲节点为当前节点
* */
public void setChildren(List<ITreeModel> children) {
if(children == null) return; //如果为null,do nothing
this.children = children;
//设置level,遍历所有的children树,然后取最大值
int max = -1;
for(int i=0; i<children.size(); i++){
children.get(i).setParent(this); //维护parent-children的相互关联关系
if(children.get(i).level() > max) max = children.get(i).level();
}
//如果添加的节点都是叶子节点,那么当前层次为2
//否则计算最大的树层次 = 子节点最大的层次 + 1
if(max != -1 && max != 0){
level += max + 1;
}else{
level = 2;
}
}
@Override
public int level() {
//每次要计算树的高度,都必须遍历整棵树的,然后确定树的高度,由于树随时可以被改变,所以这里不适合使用缓存模式
return level;
}
@Override
public List<ITreeModel> children() {
return children;
}
@Override
public ITreeModel parent() {
return parent;
}
@Override
public String nodeType() {
if(children != null){ //拥有子节点,则代表该节点是菜单
return MENU;
}else{
return LEAF;
}
}
@Override
public String url() {
return url;
}
@Override
public String id() {
return id;
}
@Override
public boolean isexpand() {
return isexpand;
}
@Override
public String name(){
return name;
}
@Override
public String toTreeJson() {
JSONObject json = new JSONObject();
//生成这个节点的基本数据
json.put("text", name);
json.put("isexpand", isexpand);
json.put("url", url);
if(parent != null){
json.put("pid", parent.id());
}
//生成这个节点的子菜单数据
JSONArray childrenJson = new JSONArray();
if(children != null){
for(ITreeModel child: children){
//让每个子menu递归的去生成json数据
childrenJson.add(toJson(child));
}
json.put("children", childrenJson);
}
return json.toString();
}
/**
* 递归入口
* @see MenuVo#toJson()
* */
private String toJson(ITreeModel tree){
JSONObject json = new JSONObject();
if(tree.children() != null){
//生成这个菜单节点的基本数据
json.put("text", tree.name());
json.put("id", tree.id());
if(tree.parent() != null){
json.put("pid", tree.parent().id());
}
json.put("isexpand", tree.isexpand());
//生成这个菜单节点的子菜单数据
JSONArray childrenJson = new JSONArray();
if(tree.children() != null){
for(ITreeModel child: tree.children()){
//让每个子menu递归的去生成json数据
childrenJson.add(toJson(child));
}
json.put("children", childrenJson);
}
}else{ //这个节点不是菜单,是菜单下面的一个具体子节点,该节点已经没有子节点了
json.put("id", tree.id());
if(tree.parent() != null){
json.put("pid", tree.parent().id());
}
json.put("text", tree.name());
json.put("url", tree.url());
}
return json.toString();
}
@Override
public List<ITreeModel> route() {
List<ITreeModel> route = new ArrayList<ITreeModel>();
ITreeModel current = this;
while(current != null){
route.add(current);
current = current.parent();
}
java.util.Collections.reverse(route);
return route;
}
@Override
public List<ITreeModel> subTree() {
List<ITreeModel> subTree = new ArrayList<ITreeModel>();
subTree.add(this);
if(this.children == null) return subTree;
Iterator<ITreeModel> sti = children.iterator();
while(sti.hasNext()){
ITreeModel tm = sti.next();
subTree.add(tm);
if(tm.children() != null) subTree.addAll(tm.subTree());
}
return subTree;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + (isexpand ? 1231 : 1237);
result = prime * result + level;
result = prime * result + (name == null? 0 : name.hashCode());
result = prime * result + ((url == null) ? 0 : url.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LigerUiTree other = (LigerUiTree) obj;
if (id == null) {
if (other.id() != null)
return false;
} else if (!id.equals(other.id()))
return false;
if (isexpand != other.isexpand())
return false;
if (level != other.level())
return false;
if(name == null){
if(other.name() != null)
return false;
}
if (url == null) {
if (other.url() != null)
return false;
} else if (!url.equals(other.url()))
return false;
return true;
}
/**
* 返回节点的基本信息
* @return
* */
@Override
public String toString() {
return "LigerUiTree [name=" + name + ", level=" + level + ", url=" + url + ", id=" + id
+ ", nodeType=" + nodeType() + ", isexpand=" + isexpand + "]";
}
}
在LigerUiTree中,我们使用了递归来遍历树。这个代码不难,就是普通的一些树的常用操作。为了测试,我们需要一个工具类,用来输出这个树的数据结果:
package net.itaem.ligerui;
import java.util.List;
/**
*
* 这个类用来输出树的结构信息,为了方便测试
* @author 骆宏
* @date 2014-08-19
* @email 846705189@qq.com
* */
public class LigerUiTreeTool {
/**
* 以下面个数来输出树
* root
* --child1
* --child2
* --child-child1
* --child-child2
* --child-3
* */
public static void printTree(ITreeModel tree){
if(tree == null) return;
if(tree.parent() == null){ //输出根节点的名字
System.out.println(tree.name());
}
List<ITreeModel> children = tree.children();
if(children != null){
level++;
for(int i=0; i<children.size(); i++){
for(int j=0; j<level; j++){
System.out.print(" ");
}
System.out.println("--" + children.get(i).name());
if(children.get(i).children() != null)
printTree(children.get(i));
else if(i == children.size()-1){
level = 1;
}
}
}
}
//用来辅助输出的一个level,用来计算输出空格
private static int level = 1;
}
上面的类是用于输出树的,没有大的实际意义。在这个工具树种,我们使用了广度遍历。一般情况下,树的遍历有两种。一种是广度遍历,另外一种则是深度遍历。大家有兴趣可以百度下相关定义以及概念。
代码差不多了,直接写个测试吧。
package net.itaem.test;
import java.util.ArrayList;
import java.util.List;
import net.itaem.ligerui.ITreeModel;
import net.itaem.ligerui.LigerUiTree;
import net.itaem.ligerui.LigerUiTreeTool;
/**
* 测试liger ui的树模型
* */
public class LigerUiTreeTest {
public static void main(String[] args){
ITreeModel root = new LigerUiTree("ROOT", "根节点", "localhost/root");
List<ITreeModel> children = new ArrayList<ITreeModel>();
//生成一个1 * 10 * 3三个层次的树模型
for(int i=0; i<10; i++){
ITreeModel child = new LigerUiTree("child-" + i, "child-name-" + i, "localhost/child-" + i);
List<ITreeModel> childChildren = new ArrayList<ITreeModel>();
for(int j=0; j<3; j++){
ITreeModel childChild = new LigerUiTree("children-" + i + "" + j, "child-children-child-" + i + "" + j, "localhost/child/children-" + i + "" + j);
childChildren.add(childChild);
}
child.setChildren(childChildren);
children.add(child);
}
root.setChildren(children);
System.out.println("======================whole tree=======================");
LigerUiTreeTool.printTree(root);
System.out.println("=======================基本信息================================");
System.out.println(root);
System.out.println("==========================subTree==============================");
//输出根节点的子树
List<ITreeModel> subTree = root.children().get(0).subTree();
System.out.println(subTree);
System.out.println("==========================route================================");
//测试叶子节点到根节点的所有节点
ITreeModel leaf = root.children().get(0).children().get(0);
System.out.println(leaf.route());
System.out.println("==========================after change=========================");
//测试修改名字,url,name,id,isexpend等属性
root.setName("change name");
root.setId("change id");
root.setIsexpand(true);
root.setUrl("change url");
System.out.println(root);
System.out.println("==============================liger ui tree json==========================");
System.out.println(root.toTreeJson());
}
}
下面是程序输出结果:
======================whole tree=======================
根节点
--child-name-0
--child-children-child-00
--child-children-child-01
--child-children-child-02
--child-name-1
--child-children-child-10
--child-children-child-11
--child-children-child-12
--child-name-2
--child-children-child-20
--child-children-child-21
--child-children-child-22
--child-name-3
--child-children-child-30
--child-children-child-31
--child-children-child-32
--child-name-4
--child-children-child-40
--child-children-child-41
--child-children-child-42
--child-name-5
--child-children-child-50
--child-children-child-51
--child-children-child-52
--child-name-6
--child-children-child-60
--child-children-child-61
--child-children-child-62
--child-name-7
--child-children-child-70
--child-children-child-71
--child-children-child-72
--child-name-8
--child-children-child-80
--child-children-child-81
--child-children-child-82
--child-name-9
--child-children-child-90
--child-children-child-91
--child-children-child-92
=======================基本信息================================
LigerUiTree [name=根节点, level=3, url=localhost/root, id=ROOT, nodeType=menu, isexpand=false]
==========================subTree==============================
[LigerUiTree [name=child-name-0, level=2, url=localhost/child-0, id=child-0, nodeType=menu, isexpand=false], LigerUiTree [name=child-children-child-00, level=0, url=localhost/child/children-00, id=children-00, nodeType=leaf, isexpand=false], LigerUiTree [name=child-children-child-01, level=0, url=localhost/child/children-01, id=children-01, nodeType=leaf, isexpand=false], LigerUiTree [name=child-children-child-02, level=0, url=localhost/child/children-02, id=children-02, nodeType=leaf, isexpand=false]]
==========================route================================
[LigerUiTree [name=根节点, level=3, url=localhost/root, id=ROOT, nodeType=menu, isexpand=false], LigerUiTree [name=child-name-0, level=2, url=localhost/child-0, id=child-0, nodeType=menu, isexpand=false], LigerUiTree [name=child-children-child-00, level=0, url=localhost/child/children-00, id=children-00, nodeType=leaf, isexpand=false]]
==========================after change=========================
LigerUiTree [name=change name, level=3, url=change url, id=change id, nodeType=menu, isexpand=true]
哈哈,测试完毕,然后就可以使用Action,将树的数据携带到jsp中,那么一个ligerui的树插件就搞定了。