TreeGrid

百科

TreeGrid是一种DHTM来自L控件,其完全使用JavaScript语言编写,用以在HTML页面上展示具有层次结构的数据项,其核心技术为多叉树。

  • 中文名称 树形表格
  • 外文名称 TreeGrid
  • 定义 是一个DHTML控件
  • 核心技术 多叉树

软件介绍

  TreeGrid是一种DHTML控件,来自其完全使用JavaScript语言编写,用以在HTML页面上展示具有层次结构的360百科数据项,其核心技术为多叉树。

软件功能

  本文主要介绍心轻盟富第那粒官此讨TreeGrid的概念和实现原理,下面这张图就是一个完整的TreeGrid,

  点击图片,查看大图。

  假设列1为A-Z这26个字母及其子节点温汽维组成的层次结构树:

  (顾名思义,TreeGrid就是树形表格)

  选择"按列2降序排序",排序后的结果如下:

  (实际上应该对所有页的数据进行排序,而不是只对当前页的数据排序,这里为了说明排序规则每社须新代专金威,只展示了A,B,C三个节点及其子节点)

  点击"下一页",

  (只对第一层节点进行分页,假设每页显示10个节点)

  关于TreeGrid的实现原理,请阅读下面这篇文章:

  强火附叉树及其应用:多叉树结合JavaScript树形控件实现代始架座支女步导括品无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)

研究原因

  一、问题研究来自的背景和意义
在Web应用程序开发领域,基于Ajax技术的JavaScript树形控件已经被广泛使用,它用来在Html页面上展现具有层次结构的数据项。目前市场上常见的JavaScript框架及组件库中均包含自己的树形控级吸顾否件,例如jQuery、Ext JS等,还有一些独立的树形控件,例如dhtmlxTree等,这些树形控件完美的解决了层次数据的展示问题。府半田运赶排吃依移防展示离不开数据,树形控件主要利用Ajax技术从服务器端获取数据源,数据源的格式主要包括JSON、X受查ML等,而这些层次数据一般都存储在数据库中。"无限级树形结构",顾名思义,没有级别的专多角系牛出节可克电今限制,它的数据通常360百科来自数据库中的无限级层次数据,这种数据的存储表通常注州包括id和parentId这两个字段,以此来表示数据之间所令设从社土缩你沙济的层次关系。现在问题来了,既然树形控件的数据源采用JSON或XML等格式的散形岩于采界指左字符串来组织层次数据,而层次数据又存储在数据库的表从掌中,那么如何建立起树形控件与层次数据之间的关系,换句话说,如何将数据卫丝重振龙检多措顾件库中的层次数据转换成对应的层次结构的JSON或XML格式的字符串,返回给客户端的JavaScript树形控件?这就是我们要解决的影府么片纪行林护关键技术问题。本文将以目前市场上比较知名的Ext J益重杀黄S框架为例,讲述实现无限级树形结构的方法,该方法同样适用于其它类似的JavaScript树形控件。

  Ext JS框架是富客户端开发中出类拔萃的框架之一。在Ext的UI控件叶二书促止足侵创化中,树形控件无疑是最为常用的控件之一,它用来实现树形结构的视图。Tre某真入急并导粒怕己要苗eNode用来实身国手湖军肥冲左吗板现静态的树形结构,AsyncTreeNode用来实现动态的异步加载树形结构,后者最为常用,它通过接收服务器端返回来的JSON格式的数据,动态生成树形结构节点。动态生成树有两种思路:一种是一次性生成全部树节点,另一种是逐级加载树节点(利用Ajax,每次点击节点时查询下一级节点)。对于大数据量的树节点来说,逐级加载是比较合适的选择,但是对于小数据量的树节点来说,一次性生成全部践年兵异校吧浓毫节点应该是最为合理的空而举边方案。在实际应用开发中,一般不会遇到特别大数据量的场景,所以一次性生成全部树节点是我们重点研究的技术点,也就是本文要解决的关键技术问题。本文以基于Ext JS的应用系统为例肥九精,讲述如何将数据库中的无限级层次数据一次性在界面中生成全部树节点(例如在界面中以树形方式一次性展示出银行所有分支机构的信息),同时对每一个层次的节点按照某一属性和规则排序,展示出有序的树形结构。

  解决一次性构造无限级树形结构的问题,可以拓展出更多的应用场景,例如树形结构表格TreeGrid,一次性生成树形表格,对树形表格进行完整分页,对表格列进行全排序;或者可以利用本文的思路扩展出其他的更复杂的应用场景。

  先看两个图例,有个直观上的认识:

  图一,银行分支机构树形结构

  图二,树形结构表格

  二、详细设计方案
让我们先看两段代码片段:

  文件一,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,

  // getBranch.do请求服务器返回多级树形结构的JSON字符串

  loader: new Ext.tree.TreeLoader({dataUrl:'getBranch.do'}),

  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: '112000',

  text: '廊坊银行解放道支行',

  children: [

  {

  id: '112200',

  text: '廊坊银行三大街支行',

  leaf: true

  },

  {

  id: '112100',

  text: '廊坊银行广阳道支行',

  leaf: true

  }

  ]

  },

  {

  id: '111000',

  text: '廊坊银行金光道支行',

  leaf: true

  }

  ]

  }

  ]

  }

  同时还需要对树中每一个层次的节点按照某一属性(比如分支机构编号)进行排序,以展示出有序的树形结构。

  现在可以把问题概括为:
1、把数据库中的层次数据转换成多级树形结构的JSON格式的字符串

  2、对树中每一个层次的节点按照某一属性(比如分支机构编号)进行排序

  下面介绍解决问题的思路:
在数据结构这门课中,我们都学过树,无限级树形结构就可以抽象成一种多叉树结构,即每个节点下包含多个子节点的树形结构,首先就需要把数据库中的层次数据转换成多叉树结构的对象树,也就是构造出一棵多叉树。

  有了数据结构,还要实现相应的算法,我们需要实现两种算法:
1、兄弟节点横向排序算法,对隶属于同一个父节点下面的所有直接子节点按照某一节点属性和规则进行排序,保持兄弟节点横向有序;

  2、先序遍历算法,递归打印出无限级JSON字符串。

  概括起来分为三步:
1、构造无序的多叉树结构

  2、实现兄弟节点横向排序方法

  3、实现先序遍历方法,打印出JSON字符串

  如图所示:

  三、源代码实现(Java版)
实现这样一颗树,需要设计两个类:树类(Tree)、节点类(Node);排序时还需要一个比较器类(NodeIDComparator);为了方便演示,还需要构造一些假的层次数据,因此还需要建一个构造假数据的类(VirtualDataGenerator),以下代码拷贝出来之后可直接运行测试:

  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 Tree {

  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)).addChild(node);

  }

  }

  // 输出无序的树形结构的JSON字符串

  System.out.println(root);

  // 对多叉树进行横向排序

  root.sortChildren();

  // 输出有序的树形结构的JSON字符串

  System.out.println(root);

  // 程序输出结果如下:

  //

  // 无序的树形结构(格式化后的结果):

  // {

  // 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;

  /**

  * 孩子节点列表

  */

  private List children = new ArrayList();

  // 添加孩子节点

  public void addChild(Node node) {

  children.add(node);

  }

  // 先序遍历,拼接JSON字符串

  public String toString() {

  String result = "{" + "id : '" + id + "'" + ", text : '" + text + "'";

  if (children.size() != 0) {

  result += ", children : [";

  for (int i = 0; i < children.size(); i++) {

  result += ((Node) children.get(i)).toString() + ",";

  }

  result = result.substring(0, result.length() - 1);

  result += "]";

  } else {

  result += ", leaf : true";

  }

  return result + "}";

  }

  // 兄弟节点横向排序

  public void sortChildren() {

  if (children.size() != 0) {

  // 对本层节点进行排序(可根据不同的排序属性,传入不同的比较器,这里 传入ID比较器)

  Collections.sort(children, new NodeIDComparator());

  // 对每个节点的下一层节点进行排序

  for (int i = 0; i < children.size(); i++) {

  ((Node) children.get(i)).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、实现对指定层次的排序(例如只排序第一层的节点,或者只排序某一父节点下的所有子节点)

  2、遍历输出树形结构时可以加入判断条件过滤掉某些节点

  3、实现节点的删除功能

  4、在节点类中增加一个父节点的引用,就可以计算出某一节点所处的级别

  5、在不支持层次查询的数据库应用系统中使用该算法实现相同的效果

  四、思考与总结
这篇文章的重点是如何构造有序的无限级的树形结构JSON字符串,一次性生成树形结构,而不是利用Ajax的方式,反复向服务器端发送请求,一级接一级的加载树节点。

  既然可以构造无限级的JSON字符串,那么也可以根据这个思路构造无限级的XML字符串,或者构造具有层次结构的UL – LI组合(用UL - LI来展示树形结构),或者构造具有层次结构的TABLE(用TABLE来展示树形结构)。如下所示:

  (1)XML层次结构

  <nodeGroup id="100000" name="廊坊银行总行">

  <nodeGroup id="110000" name="廊坊分行">

  <node id="113000" name="廊坊银行开发区支行">

  </node>

  <node id="111000" name="廊坊银行金光道支行">

  </node>

  <nodeGroup id="112000" name="廊坊银行解放道支行">

  <node id="112200" name="廊坊银行三大街支行">

  </node>

  <node id="112100" name="廊坊银行广阳道支行">

  </node>

  </nodeGroup>

  </nodeGroup>

  </nodeGroup>

  (2)UL - LI 层次结构

  <ul>

  <li>廊坊银行总行</li>

  <ul>

  <li>廊坊分行</li>

  <ul>

  <li>廊坊银行开发区支行</li>

  <li>廊坊银行解放道支行</li>

  <ul>

  <li>廊坊银行三大街支行</li>

  <li>廊坊银行广阳道支行</li>

  </ul>

  <li>廊坊银行金光道支行</li>

  </ul>

  </ul>

  </ul>

  (3)TABLE层次结构

  <table>

  <tr><td>廊坊银行总行</td></tr>

  <tr><td>&nbsp;&nbsp;廊坊分行</td></tr>

  <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;廊坊银行开发区支行</td></tr>

  <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;廊坊银行解放道支行</td></tr>

  <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;廊坊银行三大街支行</td></tr>

  <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;廊坊银行广阳道支行</td></tr>

  <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;廊坊银行金光道支行</td></tr>

  </table>

  另外对TreeGrid树形表格也有一定的价值:
1、一次性构造树形表格,实现数据分级展示

  2、通过更换比较器,实现对不同表格列的全排序(全排序指的是对所有页的数据进行排序,而不是只对当前页的数据排序;排序规则与Oracle数据库中的层次查询类似,即兄弟节点横向排序)

  3、实现对树形表格的完整分页(每次分页时,只取固定数目的第一层节点,之后调用toString方法,展示出完整条数的分级数据,即每页的记录条数是不固定的,但必须是完整的树形结构)

标签:
声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:yongganaa@126.com

评论留言

我要留言

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:yongganaa@126.com