首页 > 代码库 > liger ui组件的抽取与封装

liger ui组件的抽取与封装

       为了让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的树插件就搞定了。