Android无限级树状结构

Posted Asion Tang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

以上是关于Android无限级树状结构的主要内容,如果未能解决你的问题,请参考以下文章

PHP原生递归实现无限级分类树状展示数据

几十万条数据用PHP递归能实现无限级分类吗

数据结构:树状数组 学习笔记

无限级结构SQL查询所有的下级和所有的下级

如何用java无限级树形结构的构建

JSON数据格式生成无限级树结构