作者: ecsun  鏈接:http://papa.javaeye.com/blog/231734  發(fā)表時間: 2008年08月22日

聲明:本文系JavaEye網(wǎng)站發(fā)布的原創(chuàng)博客文章,未經(jīng)作者書面許可,嚴禁任何網(wǎng)站轉(zhuǎn)載本文,否則必將追究法律責任!

   三年前,我剛剛畢業(yè),取得了第一份工作,在經(jīng)過三個月的試用期以后,開始參考一個小項目的需求調(diào)研及前期的準備工作,由于公司人手有限,最終的編碼實現(xiàn),也落到了我身上.

 

   在做demo的時候,我遇到的第一個項工作,就是去實現(xiàn)一個無限的方便的樹形結(jié)構(gòu),因為當時的項目,是建筑節(jié)能領(lǐng)域的一個新項目,公司在這方面,沒有太多的沉淀,而公司已有的資源,是自己的一個開發(fā)平臺,叫做MISD,不知道現(xiàn)在市場上還有沒有個工具.MISD無法實現(xiàn)這樣的結(jié)構(gòu),就只有能過編碼來實現(xiàn)了.初出茅廬,經(jīng)驗不足,沒有那么的考慮,于是拿tree maker一個多小時,把項目要用的幾個樹結(jié)構(gòu),做出來了,Ctrl+c,Ctrl+v,搞定~~拿去給經(jīng)理一看:小伙子,手腳挺麻利的嘛~不錯不錯~~我先看看.

    心里挺樂~~真的挺樂,沒幾分鐘,又被leader叫了過去,這樹我在哪里加節(jié)點編輯啊?"要改代碼啊."leader聽了傻了,"那不行啊,客戶不懂代碼~",去寫一個動態(tài)的吧,存數(shù)據(jù)....

 

    連靜態(tài)的都是拿tree maker做的,動態(tài)的,我這怎么做啊?當時是真的不會做.于是到網(wǎng)上去找,還真不少.經(jīng)過對比,在struts-menu里面,有一個數(shù)據(jù)庫支持的樹結(jié)構(gòu)的例子,于是,按照文檔,做了幾天,還好,結(jié)構(gòu)出來了.基本實現(xiàn)了項目所需要的功能,而這個項目,后來里面很多的樹結(jié)構(gòu),都是通過我對struts-menu的修改實現(xiàn)的,效果還不錯.

 

  這是我第一次寫樹結(jié)構(gòu)的經(jīng)歷,整個過程中也是被leader叫去談了好幾次,好在最終沒有給項目造成影響,保證了項目的按時完成,這套結(jié)構(gòu),后來也成為公司各個項目中使用的標準樹結(jié)構(gòu),即使在離職到現(xiàn)在,這套結(jié)構(gòu),包括后來我搭建的項目開發(fā)基礎(chǔ)框架,依然在原公司各個項目中發(fā)揮著重要的作用.

 

 

  我相信很多朋友和我一樣,在剛開始做項目的時候,都或多或少的,被這種無限的樹形結(jié)構(gòu)所困擾.希望我的經(jīng)歷,可以給后來的朋友帶來一些有用的參考價值.

 

  所謂無限的權(quán)形結(jié)構(gòu),最主要的方面,主要是可以無限的添加子結(jié)節(jié),到底有沒有人會無限的去添加子節(jié)點的,在我做過的項目中,不存在這樣的問題,當節(jié)點添加的第四級的時候,已經(jīng)很少有用戶希望再點下去了,所以說,無限,只是客戶或者leader希望服務(wù)于項目的樹形結(jié)構(gòu)更靈活而提出的一種需求,因為不管是客戶也好,leader也好,在項目開始的時候,并不知道,未來需要多少級樹形結(jié)構(gòu).

 

  設(shè)計這個的樹形結(jié)構(gòu),最基礎(chǔ)的,是數(shù)據(jù)庫表的設(shè)計,要實現(xiàn)無限,數(shù)據(jù)庫需要像這樣的結(jié)構(gòu):

寫道
create table tree(
id varchar(32) primary key,
name varchar(100) not null,
parent_id varchar(32)
);

 

其中最主要的是id和parent_id兩上字段,它們定義了所有父節(jié)點與子節(jié)點之間的父子關(guān)系,這也是一種我們很常用的遞歸表結(jié)構(gòu)設(shè)計,或者在ORM叫做自身雙向一對多關(guān)系映射.現(xiàn)在oracle,SQLServer 2005都支持了遞歸SQL的查詢.不過我們不常用.

 

有了這樣的數(shù)據(jù)表結(jié)構(gòu),那么不管我們用什么技術(shù)去實現(xiàn)無限級樹形結(jié)構(gòu),都是很容易的.

 

1.struts-menu.

 

   struts-menu從本質(zhì)上來說,是一種taglib,作者把眾多的腳本以及對樹的操作,封裝成簡單的taglib,用戶在生成樹的時候,只需要簡單的幾句標簽,就可以完成整個樹形結(jié)構(gòu)的生成,比如經(jīng)典的struts-menu生成樹形結(jié)構(gòu)的頁面代碼,基本如下:

 

寫道
<menu:useMenuDisplayer name="ListMenu" repository="repository">
<c:forEach var="parent" items="${parentMenus}">
<menu:displayMenu name="${parent.title}"> </menu:displayMenu>
</c:forEach>
</menu:useMenuDisplayer>

 

其它的,基本上是一個js的import,沒有更多其它的代碼,當然,我們需要提前將樹結(jié)構(gòu)數(shù)據(jù)放入request或session中.

 

關(guān)于struts-menu的詳細實現(xiàn),可以參考我寫的另一篇博客:

 

http://papa.javaeye.com/admin/blogs/145554

 

 

2.DTree

 

 struts-menu 有一個很大的問題,就是速度,在界面上的反應(yīng)速度,明顯比較慢,當然,很多人從很多方面對struts-menu做了優(yōu)化,但優(yōu)化以后,個人覺得,還不是很理想,這個時候,我們就需要一種速度相對比較快的樹形結(jié)構(gòu)了,Dtree在方面,可以說做的相對比較好.同時,使用也很簡單,Dtree可以使用和struts-menu一樣的數(shù)據(jù)結(jié)構(gòu),只需要在里面加入一個很簡單的隨機數(shù)就可以了,在http://papa.javaeye.com/admin/blogs/145554有完整的描述 ,可以看到,如何將struts-menu的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為Dtree的數(shù)據(jù)結(jié)構(gòu).

 

Dtree在頁面中的展現(xiàn),主要需要使用少量的javascript,這些代碼,可以參考官方網(wǎng)站上的,也可以參考下面不太友好的實現(xiàn).

 

寫道
<script type="text/javascript">
<!--

d = new dTree('d');
<%
try{

List nodesCollection =(List)request.getAttribute("dTree");

Iterator it=nodesCollection.iterator();
while(it.hasNext()){
Map item=(Map) it.next();
%>
d.add(<%=item.get("dTree_id").toString()%>,<%=item.get("dTree_parentid").toString()%>,'<%=item.get("name").toString()%>','<%=item.get("location").toString()%>','','<%=item.get("target")%>');
<%
}
}catch(Exception e){
System.out.println(e.toString());
}
%>


document.write(d);

//-->
</script>

 

可以看到,dtree的實現(xiàn),也是很簡單的.

 

 

3.Ext Tree.

  

   從一年多以前,Ext 開始火爆,很多人開始使用ext tree ,也有很多人,因為ext tree 而開始使用ext,在這一塊,在我的另一篇博客(三篇連載,請在博客中查詢):

http://papa.javaeye.com/admin/blogs/157922

 

同時,在開源項目FaceYe中,使用了大量的Ext tree,包括帶多選框的Ext tree 結(jié)構(gòu),可以到http://faceye.googlecode.com 下載FaceYe代碼包并安裝查看效果,在我的博客http://papa.javaeye.com中,對FaceYe有完整的描述 .

 

有完整的描述,Ext tree使用的數(shù)據(jù)結(jié)構(gòu),與struts-menu類似.

 

但需要注意的是,ext tree的加載速度并不太好.大的項目使用需要注意.

 

4.YUI tree.

 

   說了Ext tree,再說YUI tree,似乎有點多此一舉,使用Ext的朋友,都知道Ext tree和 YUI tree可以說是師出同門,但Ext tree對YUI  tree做了比較多的擴展.也就是因為這些擴展,使得使用Ext tree 相對簡單,而使用YUI tree或許多多少少有些不便.廢話少說,先看一下如何展現(xiàn)YUI TREE:

 

 

寫道
<script type="text/javascript">

(function(){
var tree;
function loadNodeData(node,fnLoadComplete){
var callback={
success:function(o){
if(o.responseText){
var result=YAHOO.lang.JSON.parse(o.responseText);
if(result){
for(var i=0;i<result.length;i++){
var tempNode=new YAHOO.widget.TextNode(result[i],node,false);
if(result[i].isLeaf){
tempNode.isLeaf=true;
}
}
}
tree.draw();
}
o.argument.fnLoadComplete();
},
failure:function(o){
o.argument.fnLoadComplete();
},
argument:{
'node':node,
'fnLoadComplete':fnLoadComplete
},
timeout:7000
};
var req=YAHOO.util.Connect.asyncRequest('POST', base.path+'security/treeAction!tree.do', callback,'nodeId='+node.data.id);
}

function init(){
tree=new YAHOO.widget.TreeView("tree-viewer");
tree.setDynamicLoad(loadNodeData,0);
initRoot();
tree.draw();
}
function initRoot(){
var root=tree.getRoot();
var callback={
success:function(o){
var result=YAHOO.lang.JSON.parse(o.responseText);
for(var i=0;i<result.length;i++){
var node=new YAHOO.widget.TextNode(result[i],root,false);
if(result[i].isLeaf){
node.isLeaf=true;
}
}
tree.draw();
},
failure:function(o){
}
};
var req=YAHOO.util.Connect.asyncRequest('POST', base.path+'security/treeAction!tree.do', callback);
}
YAHOO.util.Event.on(window,'load',function(e){
init();
});
})();

</script>

 

后臺方面的代碼,大多都大同小異,有興趣的朋友參考FaceYe的實現(xiàn)了.

 

 



已有 0 人發(fā)表留言,猛擊->>這里<<-參與討論


JavaEye推薦