Android无限级树状结构

通过对ListView简单的扩展、再封装,即可实现无限层级的树控件TreeView。

  1 package cn.asiontang.nleveltreelistview;
  2
  3 import android.annotation.TargetApi;
  4 import android.content.Context;
  5 import android.os.Build;
  6 import android.util.AttributeSet;
  7 import android.view.View;
  8 import android.widget.AdapterView;
  9 import android.widget.ListAdapter;
 10 import android.widget.ListView;
 11
 12 /**
 13  * 支持N(无限)层级的树列表结构
 14  *
 15  * <p>参考资料:</p>
 16  * <ul>
 17  * <li>
 18  * <a href="http://item.congci.com/item/android-wuxian-ji-shuzhuang-jiegou">Android无限级树状结构 -
 19  * Android
 20  * - 从此网</a>
 21  * </li>
 22  * </ul>
 23  *
 24  * @author AsionTang
 25  * @since 2016年6月1日 18:38:43
 26  */
 27 @SuppressWarnings("unused")
 28 public class NLevelTreeView extends ListView
 29 {
 30     private OnTreeNodeClickListener mOnTreeNodeClickListener;
 31
 32     public NLevelTreeView(final Context context)
 33     {
 34         super(context);
 35         this.init();
 36     }
 37
 38     public NLevelTreeView(final Context context, final AttributeSet attrs)
 39     {
 40         super(context, attrs);
 41         this.init();
 42     }
 43
 44     public NLevelTreeView(final Context context, final AttributeSet attrs, final int defStyleAttr)
 45     {
 46         super(context, attrs, defStyleAttr);
 47         this.init();
 48     }
 49
 50     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 51     public NLevelTreeView(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes)
 52     {
 53         super(context, attrs, defStyleAttr, defStyleRes);
 54         this.init();
 55     }
 56
 57     private void init()
 58     {
 59     }
 60
 61     public void setAdapter(final NLevelTreeNodeAdapter adapter)
 62     {
 63         super.setAdapter(adapter);
 64
 65         //让 NLevelTreeNodeAdapter 处理 节点 收缩展开 动作
 66         super.setOnItemClickListener(adapter);
 67
 68         //处理当 叶子节点 被点击后的事件 回调。
 69         adapter.setOuterOnItemClickListener(new OnItemClickListener()
 70         {
 71             @Override
 72             public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id)
 73             {
 74                 if (NLevelTreeView.this.mOnTreeNodeClickListener != null)
 75                 {
 76                     final NLevelTreeNode item = (NLevelTreeNode) parent.getItemAtPosition(position);
 77                     NLevelTreeView.this.mOnTreeNodeClickListener.onTreeNodeClick(item);
 78                 }
 79             }
 80         });
 81     }
 82
 83     /**
 84      * 必须使用继承自 NLevelTreeNodeAdapter 的 适配器,否则会出现异常。
 85      */
 86     @Override
 87     public void setAdapter(final ListAdapter adapter)
 88     {
 89         if (adapter instanceof NLevelTreeNodeAdapter)
 90             this.setAdapter((NLevelTreeNodeAdapter) adapter);
 91         else
 92             throw new RuntimeException("For NLevelTreeView, use setAdapter(NLevelTreeNodeAdapter) instead of setAdapter(ListAdapter)");
 93     }
 94
 95     /**
 96      * 不支持使用此回调方式
 97      */
 98     @Override
 99     @Deprecated
100     public void setOnItemClickListener(final OnItemClickListener listener)
101     {
102         //实际的事件回调在setAdapter里设置,由 setOnTreeNodeClickListener 处理。
103         //super.setOuterOnItemClickListener(listener);
104
105         throw new RuntimeException("For NLevelTreeView, use setOnTreeNodeClickListener() instead of setOnItemClickListener()");
106     }
107
108     /**
109      * 默认只支持叶子节点的Click事件
110      */
111     public void setOnTreeNodeClickListener(final OnTreeNodeClickListener listener)
112     {
113         this.mOnTreeNodeClickListener = listener;
114     }
115
116     /**
117      * 默认只支持叶子节点的Click事件
118      */
119     public interface OnTreeNodeClickListener
120     {
121         /**
122          * 默认只支持叶子节点的Click事件
123          */
124         void onTreeNodeClick(NLevelTreeNode node);
125     }
126 }

NLevelTreeView.java

 1 package cn.asiontang.nleveltreelistview;
 2
 3 import android.content.Context;
 4 import android.view.View;
 5 import android.widget.AdapterView;
 6
 7 import java.util.HashSet;
 8 import java.util.List;
 9 import java.util.Set;
10
11 /**
12  * @author AsionTang
13  * @since 2016年6月1日 18:38:43
14  */
15 @SuppressWarnings("unused")
16 public abstract class NLevelTreeNodeAdapter extends BaseAdapterEx3<NLevelTreeNode> implements AdapterView.OnItemClickListener
17 {
18     private final Set<NLevelTreeNode> mExpandedNodeList = new HashSet<>();
19     private AdapterView.OnItemClickListener mOuterOnItemClickListener;
20
21     public NLevelTreeNodeAdapter(final Context context, final int itemLayoutResId)
22     {
23         super(context, itemLayoutResId);
24     }
25
26     public NLevelTreeNodeAdapter(final Context context, final int itemLayoutResId, final List<NLevelTreeNode> objects)
27     {
28         super(context, itemLayoutResId, objects);
29     }
30
31     @Override
32     public void convertView(final ViewHolder viewHolder, final NLevelTreeNode item)
33     {
34         this.convertView(viewHolder, item, this.mExpandedNodeList.contains(item));
35     }
36
37     public abstract void convertView(final ViewHolder viewHolder, final NLevelTreeNode item, boolean isExpanded);
38
39     @Override
40     public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id)
41     {
42         final NLevelTreeNode item = getItem(position);
43         if (this.mExpandedNodeList.contains(item))
44         {
45             //把展开的节点,收缩起来
46             this.mExpandedNodeList.remove(item);
47
48             final int nextPosition = position + 1;
49             while (true)
50             {
51                 //说明已经删除到最后一个节点了。
52                 if (nextPosition >= this.getOriginaItems().size())
53                     break;
54
55                 final NLevelTreeNode tmpNode = this.getOriginaItems().get(nextPosition);
56
57                 //只删除比它自己级别深的节点(如它的子、孙、重孙节点)
58                 if (tmpNode.getLevel() <= item.getLevel())
59                     break;
60
61                 this.getOriginaItems().remove(tmpNode);
62
63                 //防止它的子孙节点也有可能是展开的,所有也要移除其状态。
64                 this.mExpandedNodeList.remove(tmpNode);
65             }
66             this.refresh();
67         }
68         else
69         {
70             //没有子节点,则不允许展开
71             if (item.getChilds().size() == 0)
72             {
73                 //默认只支持叶子节点的Click事件
74                 if (this.mOuterOnItemClickListener != null)
75                     this.mOuterOnItemClickListener.onItemClick(parent, view, position, id);
76                 return;
77             }
78
79             //把收缩的节点,展开起来
80             this.mExpandedNodeList.add(item);
81
82             this.getOriginaItems().addAll(position + 1, item.getChilds());
83
84             this.refresh();
85         }
86     }
87
88     /**
89      * 设置外围调用者真正需要的 项点击OnItemClickListener 事件 回调。
90      */
91     protected void setOuterOnItemClickListener(final AdapterView.OnItemClickListener listener)
92     {
93         this.mOuterOnItemClickListener = listener;
94     }
95 }

NLevelTreeNodeAdapter.java

  1 package cn.asiontang.nleveltreelistview;
  2
  3 import java.util.ArrayList;
  4 import java.util.List;
  5
  6 /**
  7  * @author AsionTang
  8  * @since 2016年6月1日 18:38:43
  9  */
 10 @SuppressWarnings("unused")
 11 public class NLevelTreeNode
 12 {
 13     private final List<NLevelTreeNode> mChilds = new ArrayList<>();
 14     private CharSequence mId;
 15     private int mLevel = 0;
 16     private CharSequence mName;
 17     private NLevelTreeNode mParentNode;
 18
 19     public NLevelTreeNode()
 20     {
 21     }
 22
 23     public NLevelTreeNode(final NLevelTreeNode parentNode, final int level, final CharSequence id, final CharSequence name)
 24     {
 25         this.setParentNode(parentNode);
 26         this.setLevel(level);
 27         this.setID(id);
 28         this.setName(name);
 29     }
 30
 31     public NLevelTreeNode(final int level, final CharSequence id, final CharSequence name)
 32     {
 33         this(null, level, id, name);
 34     }
 35
 36     public NLevelTreeNode(final CharSequence id, final CharSequence name)
 37     {
 38         this(null, 0, id, name);
 39     }
 40
 41     public NLevelTreeNode(final CharSequence name)
 42     {
 43         this(null, 0, name, name);
 44     }
 45
 46     /**
 47      * 为此Node添加一个子节点
 48      */
 49     public NLevelTreeNode addChild(final NLevelTreeNode child)
 50     {
 51         if (!this.mChilds.contains(child))
 52         {
 53             this.mChilds.add(child);
 54             child.setParentNode(this);
 55         }
 56         return this;
 57     }
 58
 59     /**
 60      * 设置此Node所属的所有子节点
 61      */
 62     public NLevelTreeNode addChilds(final List<NLevelTreeNode> childs)
 63     {
 64         for (final NLevelTreeNode child : childs)
 65             this.addChild(child);
 66         return this;
 67     }
 68
 69     /**
 70      * 获取此Node指定位置的子节点
 71      */
 72     public NLevelTreeNode getChild(final int index)
 73     {
 74         return this.mChilds.get(index);
 75     }
 76
 77     /**
 78      * 获取此Node所属的所有子节点
 79      */
 80     public List<NLevelTreeNode> getChilds()
 81     {
 82         return this.mChilds;
 83     }
 84
 85     /**
 86      * 获取当前Node 唯一标识符(当此Node被点击时,可供区分被点击的是谁)
 87      */
 88     public CharSequence getID()
 89     {
 90         return this.mId;
 91     }
 92
 93     /**
 94      * 设置当前Node 唯一标识符(当此Node被点击时,可供区分被点击的是谁)
 95      */
 96     public NLevelTreeNode setID(final CharSequence id)
 97     {
 98         this.mId = id;
 99         return this;
100     }
101
102     /**
103      * 获取当前Node所属哪个层级;一般从0级(根节点)开始递增。
104      */
105     public int getLevel()
106     {
107         return this.mLevel;
108     }
109
110     /**
111      * 设置当前Node所在的层级;一般从0级(根节点)开始递增。
112      */
113     public NLevelTreeNode setLevel(final int level)
114     {
115         this.mLevel = level;
116
117         //必须立即更新子节点的级别,否则就乱套了。
118         for (final NLevelTreeNode child : this.mChilds)
119             child.setLevel(level + 1);
120         return this;
121     }
122
123     /**
124      * 获取当前Node 名字
125      */
126     public CharSequence getName()
127     {
128         return this.mName;
129     }
130
131     /**
132      * 设置当前Node 名字
133      */
134     public NLevelTreeNode setName(final CharSequence name)
135     {
136         this.mName = name;
137         return this;
138     }
139
140     /**
141      * 获取 此Note 的父节点
142      */
143     public NLevelTreeNode getParentNode()
144     {
145         return this.mParentNode;
146     }
147
148     /**
149      * 设置 此Note 的父节点
150      */
151     public NLevelTreeNode setParentNode(final NLevelTreeNode parentNode)
152     {
153         this.mParentNode = parentNode;
154         if (parentNode != null)
155         {
156             parentNode.addChild(this);
157             this.setLevel(parentNode.getLevel() + 1);
158         }
159         return this;
160     }
161 }

NLevelTreeNode.java

源码:

https://bitbucket.org/AsionTang/75.nleveltreeview/overview
https://github.com/asiontang/75.NLevelTreeView

时间: 2024-10-23 20:53:07

Android无限级树状结构的相关文章

在Silverlight中使用HierarchicalDataTemplate为TreeView实现递归树状结构

将实体绑定到TreeView控件,实现树状结构的显示,如下图所示.这个功能通过HierarchicalDataTemplate实现. ? 1. 业务实体 作为举例,我定义了一个大家都很熟悉的Folder类型,即文件夹.我们都知道,文件夹又可以包含子文件夹,而且可以多层嵌套.所以,这是一个递归的结构体. public class Folder { public string Name { get; set; } public ObservableCollection<Folder> Folder

c# 递归、树状结构

1.树状结构 treeView.Nodes.Clear(); TreeNode tree = new TreeNode(); tree.Text = "字母"; treeView.Nodes.Add(tree); // tree.Nodes.Add("A"); TreeNode tree1 = new TreeNode(); tree1.Text = "汉字"; treeView.Nodes.Add(tree1); TreeNode a = ne

json格式转树状结构

转自:http://rockyuse.iteye.com/blog/1541308 /** * json格式转树状结构 * @param {json} json数据 * @param {String} id的字符串 * @param {String} 父id的字符串 * @param {String} children的字符串 * @return {Array} 数组 */ function transData(a, idStr, pidStr, chindrenStr){ var r = []

Java文件目录树状结构:控制台打印某个文件夹下的文件目录树状结构

1 package com.zhen.file; 2 3 import java.io.File; 4 5 /* 6 * 控制台打印某个文件夹下的文件目录树状结构 7 * 递归算法 8 */ 9 10 public class FileTree { 11 12 public static void main(String[] args) { 13 File file =new File("D:/Github/JavaTest"); 14 PrintFile(file, 0); 15 }

lua 如何输出树状结构的table?

为了让游戏前端数据输出更加条理,做了一个简单树状结构来打印数据. ccmlog.lua local function __tostring(value, indent, vmap) local str = '' indent = indent or '' vmap = vmap or {} --递归结束条件 if (type(value) ~= 'table') then if (type(value) == 'string') then --字符串 str = string.format("[

分享使用NPOI导出Excel树状结构的数据,如部门用户菜单权限

大家都知道使用NPOI导出Excel格式数据 很简单,网上一搜,到处都有示例代码. 因为工作的关系,经常会有处理各种数据库数据的场景,其中处理Excel 数据导出,以备客户人员确认数据,场景很常见. 一个系统开发出来,系统要运行起来,很多数据要初始化,这个时候也是需要客户提供各种业务的基础数据.客户提供的数据中,其中除了word.pdf,最常见的就是Excel. 废话不多说,直接上图上代码: 如图, 左侧三列,作为 一个系统 所有菜单的树状结构. 其他列 以用户的信息(如用户名.登录名) 作为表

树状结构Java模型、层级关系Java模型、上下级关系Java模型与html页面展示

树状结构Java模型.层级关系Java模型.上下级关系Java模型与html页面展示 一.业务原型:公司的组织结构.传销关系网 二.数据库模型 很简单,创建 id 与 pid 关系即可.(pid:parent_id) 三.Java模型 (我们把这张网撒在html的一张表里.其实用ul来展示会简单N多,自己思考为什么LZ会选择放在表里) private class Table {        private Long id; // 当前对象的id         private int x; /

PostgreSQL递归查询实现树状结构查询

在Postgresql的使用过程中发现了一个很有意思的功能,就是对于需要类似于树状结构的结果可以使用递归查询实现.比如说我们常用的公司部门这种数据结构,一般我们设计表结构的时候都是类似下面的SQL,其中parent_id为NULL时表示顶级节点,否则表示上级节点ID. CREATE TABLE DEPARTMENT ( ID INTEGER PRIMARY KEY, NAME VARCHAR(32), PARENT_ID INTEGER REFERENCES DEPARTMENT(ID) );

算法 - 树状结构

写入速度 > 读取速度 存储树状结构(上)─领接表方式 写入速度 < 读取速度 1.存储树状结构(下)─预排序遍历树方式 2.预排序遍历树算法(非递归无限极分类算法)学习笔记