精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-02-01
最后修改:2012-05-20
利用多叉树实现Ext JS中的无限级树形菜单 (一种构建多级有序树形结构JSON的方法)
一、问题研究的背景和意义
解决Ext JS无限级树形菜单的问题,可以拓展出更多的应用场景,例如BI(商业智能)系统中的报表分析中的数据钻取功能,也就是多级数据列表的展示,同时对多级数据列表按照某一列数据进行排序;或者可以利用本文的思路扩展出其他的更复杂的应用场景。
先看两个图例,有个直观上的认识:
图二,BI(商业智能)树形结构报表
二、详细设计方案
文件一,branchTree.html (Ext树形控件页面) Ext.onReady( function(){ var tree = new Ext.tree.TreePanel({ height: 300, width: 400, animate:true, enableDD:true, containerScroll: true, rootVisible: false, frame: true, loader: new Ext.tree.TreeLoader({dataUrl:'getBranch.do'}), // getBranch.do请求服务器返回无限级的JSON字符串 root : new Ext.tree.AsyncTreeNode({id:'0',text:'根结点'}) }); tree.expandAll(); } );
文件二,branchTreeJSON.jsp (接收getBranch.do请求,返回无限级JSON字符串) <% // 读取银行分支机构的层次数据 List result = DataAccess.getBankInfoList(); // 将层次数据转换为双历树对象(本文下面会详细介绍该数据结构的实现方法) Node root = ExtTreeHelper.createExtTree(result); %> [ <%=root.toString()%> <!-- 以JSON的形式返回响应数据,Ext.tree.TreeLoader会根据此数据生成树形菜单 --> ]
以上两个程序文件是一次性生成无限级树形菜单所必须的,其中最为关键的部分就是如何生成一个无限级的JSON字符串,返回给客户端的Ext树形控件。对于银行分支机构来说,需要返回类似如下的JSON串: { id : '100000', text : '廊坊银行总行', children : [ { id : '110000', text : '廊坊分行', children : [ { id : '113000', text : '廊坊银行开发区支行', leaf : true }, { id : '111000', text : '廊坊银行金光道支行', leaf : true }, { id : '112000', text : '廊坊银行解放道支行', children : [ { id : '112200', text : '廊坊银行三大街支行', leaf : true }, { id : '112100', text : '廊坊银行广阳道支行', leaf : true } ] } ] } ] }
同时还可能需要对树中每一个层次的节点按照某一属性(比如分支机构编号)进行排序,以展示出有序的树形菜单。
现在可以把问题概括为: 1、 把数据库中的层次数据转换成JSON格式的字符串 2、 对树中每一个层次的节点按照某一属性(比如分支机构编号)进行排序
下面介绍解决问题的思路: 我们都学过数据结构,即组织大量数据的方法,无限级树形菜单就可以抽象成一种多叉树结构,即每个节点下包含多个子节点的树形结构,首先就需要把数据库中的层次数据转换成多叉树结构的对象树,也就是构造出一棵多叉树。
概括起来分为三步:
如图所示:
三、源代码实现(Java语言版)
package test; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Collections; /** * 树类 */ public class ExtTree { public static void main(String[] args) { // 读取层次数据结果集列表 List dataList = VirtualDataGenerator.getVirtualResult(); // 节点列表(哈希表,用于临时存储节点对象) HashMap nodeList = new HashMap(); // 根节点 Node root = null; // 根据结果集构造节点列表(存入哈希表) for (Iterator it = dataList.iterator(); it.hasNext();) { Map dataRecord = (Map) it.next(); Node node = new Node(); node.id = (String) dataRecord.get("id"); node.text = (String) dataRecord.get("text"); node.parentId = (String) dataRecord.get("parentId"); nodeList.put(node.id, node); } // 构造无序的多叉树 Set entrySet = nodeList.entrySet(); for (Iterator it = entrySet.iterator(); it.hasNext();) { Node node = (Node) ((Map.Entry) it.next()).getValue(); if (node.parentId == null || node.parentId.equals("")) { root = node; } else { ((Node) nodeList.get(node.parentId)).children.addChild(node); } } // 输出无序的树形菜单的JSON字符串 System.out.println(root.toString()); // 对多叉树进行横向排序 root.sortChildren(); // 输出有序的树形菜单的JSON字符串 System.out.println(root.toString()); // 程序输出结果如下(无序的树形菜单)(格式化后的结果): // { // id : '100000', // text : '廊坊银行总行', // children : [ // { // id : '110000', // text : '廊坊分行', // children : [ // { // id : '113000', // text : '廊坊银行开发区支行', // leaf : true // }, // { // id : '111000', // text : '廊坊银行金光道支行', // leaf : true // }, // { // id : '112000', // text : '廊坊银行解放道支行', // children : [ // { // id : '112200', // text : '廊坊银行三大街支行', // leaf : true // }, // { // id : '112100', // text : '廊坊银行广阳道支行', // leaf : true // } // ] // } // ] // } // ] // } // 程序输出结果如下(有序的树形菜单)(格式化后的结果): // { // id : '100000', // text : '廊坊银行总行', // children : [ // { // id : '110000', // text : '廊坊分行', // children : [ // { // id : '111000', // text : '廊坊银行金光道支行', // leaf : true // }, // { // id : '112000', // text : '廊坊银行解放道支行', // children : [ // { // id : '112100', // text : '廊坊银行广阳道支行', // leaf : true // }, // { // id : '112200', // text : '廊坊银行三大街支行', // leaf : true // } // ] // }, // { // id : '113000', // text : '廊坊银行开发区支行', // leaf : true // } // ] // } // ] // } } } /** * 节点类 */ class Node { /** * 节点编号 */ public String id; /** * 节点内容 */ public String text; /** * 父节点编号 */ public String parentId; /** * 孩子节点列表 */ public Children children = new Children(); // 深度优先先序遍历,拼接JSON字符串 public String toString() { String result = "{" + "id : '" + id + "'" + ", text : '" + text + "'"; if (children != null && children.getSize() != 0) { result += ", children : " + children.toString(); } else { result += ", leaf : true"; } return result + "}"; } // 广度优先遍历,对子节点进行横向排序 public void sortChildren() { if (children != null && children.getSize() != 0) { children.sortChildren(); } } } /** * 孩子列表类 */ class Children { public List list = new ArrayList(); public int getSize() { return list.size(); } public void addChild(Node node) { list.add(node); } // 拼接孩子节点的JSON字符串 public String toString() { String result = "["; for (Iterator it = list.iterator(); it.hasNext();) { result += ((Node) it.next()).toString(); result += ","; } result = result.substring(0, result.length() - 1); result += "]"; return result; } // 孩子节点排序 public void sortChildren() { // 对本层节点进行排序 // 可根据不同的排序属性,传入不同的比较器,这里传入ID比较器 Collections.sort(list, new NodeIDComparator()); // 对每个节点的下一层节点进行排序 for (Iterator it = list.iterator(); it.hasNext();) { ((Node) it.next()).sortChildren(); } } } /** * 节点比较器 */ class NodeIDComparator implements Comparator { // 按照节点编号排序 public int compare(Object o1, Object o2) { int j1 = Integer.parseInt(((Node)o1).id); int j2 = Integer.parseInt(((Node)o2).id); return (j1 < j2 ? -1 : (j1 == j2 ? 0 : 1)); } } /** * 构造虚拟的层次数据 */ class VirtualDataGenerator { // 构造无序的结果集列表,实际应用中,该数据应该从数据库中查询获得; public static List getVirtualResult() { List dataList = new ArrayList(); HashMap dataRecord1 = new HashMap(); dataRecord1.put("id", "112000"); dataRecord1.put("text", "廊坊银行解放道支行"); dataRecord1.put("parentId", "110000"); HashMap dataRecord2 = new HashMap(); dataRecord2.put("id", "112200"); dataRecord2.put("text", "廊坊银行三大街支行"); dataRecord2.put("parentId", "112000"); HashMap dataRecord3 = new HashMap(); dataRecord3.put("id", "112100"); dataRecord3.put("text", "廊坊银行广阳道支行"); dataRecord3.put("parentId", "112000"); HashMap dataRecord4 = new HashMap(); dataRecord4.put("id", "113000"); dataRecord4.put("text", "廊坊银行开发区支行"); dataRecord4.put("parentId", "110000"); HashMap dataRecord5 = new HashMap(); dataRecord5.put("id", "100000"); dataRecord5.put("text", "廊坊银行总行"); dataRecord5.put("parentId", ""); HashMap dataRecord6 = new HashMap(); dataRecord6.put("id", "110000"); dataRecord6.put("text", "廊坊分行"); dataRecord6.put("parentId", "100000"); HashMap dataRecord7 = new HashMap(); dataRecord7.put("id", "111000"); dataRecord7.put("text", "廊坊银行金光道支行"); dataRecord7.put("parentId", "110000"); dataList.add(dataRecord1); dataList.add(dataRecord2); dataList.add(dataRecord3); dataList.add(dataRecord4); dataList.add(dataRecord5); dataList.add(dataRecord6); dataList.add(dataRecord7); return dataList; } }
好了,通过上面的代码,就可以实现树的兄弟节点横向排序和深度遍历了,实现了将层次数据转换为有序无限级JSON字符串的目的。
在实际的项目中,可以把上面的有效代码融入其中,或者在此基础上进行一些扩展: 1、 实现对指定层次的排序(例如只排序第一层的节点,或者只排序某一父节点下的所有子节点)
四、思考与总结
既然可以构造无限级的JSON字符串,那么也可以根据这个思路构造无限级的XML字符串,或者构造具有层次结构的TABLE(用TABLE来展示树形结构)。如下所示:
(1)XML层次结构 <menuGroup id="100000" name="廊坊银行总行"> <menuGroup id="110000" name="廊坊分行"> <menu id="113000" name="廊坊银行开发区支行"> </menu> <menu id="111000" name="廊坊银行金光道支行"> </menu> <menuGroup id="112000" name="廊坊银行解放道支行"> <menu id="112200" name="廊坊银行三大街支行"> </menu> <menu id="112100" name="廊坊银行广阳道支行"> </menu> </menuGroup> </menuGroup> </menuGroup>
(2)TABLE层次结构 <table> <tr><td>廊坊银行总行</td></tr> <tr><td> 廊坊分行</td></tr> <tr><td> 廊坊银行开发区支行</td></tr> <tr><td> 廊坊银行解放道支行</td></tr> <tr><td> 廊坊银行三大街支行</td></tr> <tr><td> 廊坊银行广阳道支行</td></tr> <tr><td> 廊坊银行金光道支行</td></tr> </table>
同时对BI(商业智能)系统的报表分析也有一定的价值: 1、 一次性构造多级数据列表,实现数据钻取功能 2、 通过更换比较器,实现对不同指标的排序 3、 实现对多级数据列表的完整分页(每次分页时,只取固定数目的第一层节点,之后调用toString方法,展示出完整条数的分级数据)
五、参考书籍 2、Bruce Eckel,Thinking In Java Third Edition 3、David Flanagan,JavaScript: The Definitive Guide, 5th Edition 4、OCA Oracle Database 11g SQL Fundamentals I Exam Guide
六、联系方式
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 4025 次