Android 可單選多選的任意層級樹形控件


花了幾天研究了下鴻揚大神的博客《Android打造任意層級樹形控件,考驗你的數據結構和設計》,再結合公司項目改造改造,現在做個筆記。

先看看Demo的實現效果。首先看的是多選效果

再看看單選效果圖。

(不好意思,還沒學會整動態圖,兩張圖片看不出什么區別哈)

 

 

先回顧下數據結構中樹的幾個重要概念。

(1)一棵樹是N個節點和N-1條邊的集合。

(2)除去根節點外,每一個節點都有一個父親,每條邊都將某個節點連接到它的父親。

(3)一棵樹的深度等於它的最深的樹葉的深度;該深度總是等於這棵樹的高度

 

將要打造的樹形控件本質上是一個listView,既然是樹形的,那么listView的item本質上其實就是樹的節點,所以每個item都得具備一下樹節點的屬性吧,比如說它的父節點是誰?兒子節點都有哪些等等,所以我們需要將我們從服務器接收回來的數據轉化成節點模式的數據,這里就新建一個類Node。

先看看Node類必不可少的幾個屬性吧,一個是自身標志id、一個是父輩標志pid,還有一個是你需要在頁面展示的內容,比如說你這個樹形控件展示的機構部門,那么這個name就是機構名稱,如果這個屬性控件展示的人員,那么這個name就是人員的姓名,這里我把id,pid都設置成字符串類型,主要是為了防止有時候id,pid可能是非int類型的數據,比如說帶字符串的id,pid或者超過2147483647的數字。所以索性就將id,pid設置成字符串類型。

  1 package com.example.keranbin.testdemo.treeHelp;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 /**
  7  * Created by keranbin on 2016/8/30.
  8  */
  9 public class Node {
 10     //id
 11     private String id;
 12 
 13     //父輩id
 14     private String pid = "";
 15 
 16     //要在樹形控件上展示的內容,比如說機構名稱。
 17     private String name;
 18 
 19     //樹的層級
 20     private int level;
 21 
 22     // 是否是展開的
 23     private boolean isExpand = false;
 24     
 25     //是否是選擇的
 26     private boolean isChoose=false;
 27 
 28     //縮進圖標
 29     private int icon;
 30 
 31     //父親節點
 32     private Node parent;
 33 
 34     //兒子節點集合
 35     private List<Node> children = new ArrayList<>();
 36     
 37     
 38 
 39 
 40     public Node(String id, String pid, String name) {
 41         this.id = id;
 42         this.pid = pid;
 43         this.name = name;
 44 
 45     }
 46 
 47     public String getId() {
 48         return id;
 49     }
 50 
 51     public void setId(String id) {
 52         this.id = id;
 53     }
 54 
 55     public String getPid() {
 56         return pid;
 57     }
 58 
 59     public void setPid(String pid) {
 60         this.pid = pid;
 61     }
 62 
 63     public String getName() {
 64         return name;
 65     }
 66 
 67     public void setName(String name) {
 68         this.name = name;
 69     }
 70 
 71 
 72     /**
 73      * 得到當前節點的層級,如果當前節點沒有父節點,則該節點
 74      *是根節點,否則當前節點的層級為當前節點父節點的層級加1
 75      * @return
 76      */
 77     public int getLevel() {
 78         return parent == null ? 0 : parent.getLevel() + 1;
 79     }
 80 
 81     public void setLevel(int level) {
 82         this.level = level;
 83     }
 84 
 85     public boolean isExpand() {
 86         return isExpand;
 87     }
 88 
 89     /**
 90      * 設置當前節點是否展開,如果是false,那么遞歸關閉當前節點的所有子節點
 91      *
 92      * @param expand
 93      */
 94     public void setExpand(boolean expand) {
 95         isExpand = expand;
 96         if (!expand) {
 97             for (Node node : children) {
 98                 node.setExpand(false);
 99             }
100         }
101     }
102 
103     public boolean isChoose() {
104         return isChoose;
105     }
106 
107     public void setChoose(boolean choose) {
108         isChoose = choose;
109     }
110 
111     public int getIcon() {
112         return icon;
113     }
114 
115     public void setIcon(int icon) {
116         this.icon = icon;
117     }
118 
119     public Node getParent() {
120         return parent;
121     }
122 
123     public void setParent(Node parent) {
124         this.parent = parent;
125     }
126 
127     public List<Node> getChildren() {
128         return children;
129     }
130 
131     public void setChildren(List<Node> children) {
132         this.children = children;
133     }
134 
135 
136     /**
137      * 是否是根節點
138      */
139     public boolean isRoot() {
140         return parent == null;
141     }
142 
143     /**
144      * 是否是展開狀態,
145      */
146     public boolean isParentExpand() {
147         if (parent == null)
148             return false;
149         return parent.isExpand();
150     }
151 
152 
153     /**
154      * 是否是葉子節點
155      *
156      * @return
157      */
158     public boolean isLeft() {
159         return children.size() == 0;
160     }
161 }

Node類已經打造完畢啦,那么我們如何將服務器端取回的bean轉化成我們的Node類呢?答案是通過注解+反射。

 1 package com.example.keranbin.business.ccsq.annotion;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * Created by keranbin on 2016/8/30.
10  */
11 
12 
13 @Target(ElementType.FIELD)
14 @Retention(RetentionPolicy.RUNTIME)
15 public @interface TreeNodeId {
16 }
 1 package com.example.keranbin.business.ccsq.annotion;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * Created by keranbin on 2016/8/30.
10  */
11 @Target(ElementType.FIELD)
12 @Retention(RetentionPolicy.RUNTIME)
13 public @interface TreeNodePid {
14 }
 1 package com.example.keranbin.business.ccsq.annotion;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * Created by keranbin on 2016/8/30.
10  */
11 @Target(ElementType.FIELD)
12 @Retention(RetentionPolicy.RUNTIME)
13 public @interface TreeNodeLabel {
14 }

我們新建一個工具類TreeHelp,先看看怎么將bean轉化成Node。

 1    /**
 2      * 將服務器端取回的數據轉化成Node
 3      * @param datas
 4      * @return
 5      * @throws IllegalAccessError
 6      */
 7     public static <T> List<Node> convertDataToNodes(List<T> datas) throws IllegalAccessException {
 8         List<Node> nodes = new ArrayList<>();
 9         Node node = null;
10         for (T t : datas) {
11             String id = "";
12             String pid = "";
13             String label = null;
14             String type=null;
15             node = null;
16             Class c = t.getClass();
17             Field fields[] = c.getDeclaredFields();
18 
19             for (Field field : fields) {
20                 if (field.getAnnotation(TreeNodeId.class) != null) {
21                     //設置訪問權限,強制性的可以訪問
22                     field.setAccessible(true);
23                     id= (String) field.get(t);
24                 }
25 
26                 if (field.getAnnotation(TreeNodePid.class) != null) {
27                     //設置訪問權限,強制性的可以訪問
28                     field.setAccessible(true);
29                     pid= (String) field.get(t);
30                 }
31 
32                 if (field.getAnnotation(TreeNodeLabel.class) != null) {
33                     //設置訪問權限,強制性的可以訪問
34                     field.setAccessible(true);
35                     label = (String) field.get(t);
36                 }
38             node = new Node(id, pid, label,type);
39             nodes.add(node);
40         }

這時候我們已經將bean轉化成Node啦,但是我們現在所得到的Node集合中的各個Node是毫無關聯的,父節點並不知道誰是他的兒子,兒子節點也不知道誰是他的父親。所以我們得處理下

 1    /**
 2          * 循環對比兩個Node,設置節點間關聯關系
 3          */
 4         for (int i = 0; i < nodes.size(); i++) {
 5             Node n = nodes.get(i);
 6             for (int j = i+1; j < nodes.size(); j++) {
 7                 Node m = nodes.get(j);
 8                 if (m.getId().equals( n.getPid())) {//m是n的父節點
 9                     m.getChildren().add(n);
10                     n.setParent(m);
11                 } else if (m.getPid().equals( n.getId())) {//n是m的父節點
12                     n.getChildren().add(m);
13                     m.setParent(n);
14                 }
15             }
16         }

除此之外,我們還得為節點設置圖標。

 1  /**
 2      * 為節點設置圖標
 3      * 邏輯:(1)如果當前節點有孩子節點並且處於展開狀態,那么設置向下的圖標
 4      * (2)如果當前節點有孩子節點並且處於閉合狀態,那么設置向右的圖標
 5      * (3)如果當前節點沒有孩子節點,傳參-1,到時候判斷是-1,不設置圖標
 6      * @param n
 7      */
 8     private static void setNodeIcon(Node n) {
 9         if (n.getChildren().size() > 0 && n.isExpand()) {
10             n.setIcon(R.mipmap.tree_ex);
11         } else if (n.getChildren().size() > 0 && !n.isExpand()) {
12             n.setIcon(R.mipmap.tree_ec);
13         } else {
14             n.setIcon(-1);
15         }
16     }

這時候我們已經設置好節點的關聯關系啦,但是,這個時候的nodes是雜亂無章的,試想一下,我們不可能一進這個頁面從根節點到所有的葉子節點都展示給用戶吧,要是樹的層級少那還樂觀,但是要是樹的層級是100,1000呢,所以我們需要給Nodes集合中的Node排一下序,然后還需要設置一個方法來過濾出可見的節點。

 1 **
 2      * 得到排序后的Nodes
 3      * @param datas
 4      * @param <T>
 5      * @return
 6      */
 7     public static <T> List<Node> getSortedNodes(List<T> datas, int defaultExpandLevel) throws IllegalAccessException {
 8         List<Node> result = new ArrayList<>();
 9         List<Node> nodes = convertDataToNodes(datas);
10         //獲取樹的根節點
11         List<Node> rootNodes = getRootNodes(nodes);
12 
13         for (Node node : rootNodes) {
14             addNode(result, node, defaultExpandLevel, 1);
15         }
16         return result;
17     }
 /**
     * 從所有節點中過濾出根節點
     * @param nodes
     * @return
     */
    private static List<Node> getRootNodes(List<Node> nodes) {
        List<Node> root = new ArrayList<>();
        for (Node node : nodes) {
            if (node.isRoot()) {
                root.add(node);
            }
        }
        return root;
    }
 1  /**
 2      * 把一個節點的所有孩子節點都放入result
 3      * @param result
 4      * @param node               當前節點
 5      * @param defaultExpandLevel 默認初始化是展開幾層
 6      * @param currentLevel       當前節點層級
 7      */
 8     private static void addNode(List<Node> result, Node node, int defaultExpandLevel, int currentLevel) {
 9         result.add(node);
10         //如果默認展開層級大於或者當前節點層級,那么設置當前層級是展開的,否則設置是閉合的
11         if (defaultExpandLevel >= currentLevel){
12             node.setExpand(true);
13         }
14 
15         //如果當前節點已經是葉子節點,那么不需要做任何處理啦
16         if(node.isLeft()){
17             return;
18 
19         }else{
20             //如果當前節點不是葉子節點,遞歸循環遍歷不斷的添加子節點
21             for(int i=0;i<node.getChildren().size();i++){
22                 addNode(result,node.getChildren().get(i),defaultExpandLevel,currentLevel+1);
23             }
24         }
25     }
 1   /**
 2      * 過濾出可見的節點
 3      * @param nodes
 4      * @return
 5      */
 6     public static List<Node> filterVisibleNodes(List<Node> nodes){
 7         List<Node> visibleNodes=new ArrayList<>();
 8         for (Node node:nodes){
 9             //如果當前節點是根節點或者當前節點的父節點是展開的
10             if (node.isRoot()||node.isParentExpand()){
11                 setNodeIcon(node);
12                 visibleNodes.add(node);
13             }
14         }
15         return visibleNodes;
16     }

至此,我們的treeHelp類打造完畢,整個類的代碼如下。

  1 package com.example.keranbin.business.help.treeHelp;
  2 
  3 import com.example.keranbin.business.R;
  4 import com.example.keranbin.business.ccsq.annotion.TreeNodeId;
  5 import com.example.keranbin.business.ccsq.annotion.TreeNodeLabel;
  6 import com.example.keranbin.business.ccsq.annotion.TreeNodePid;
  7 import com.example.keranbin.business.ccsq.annotion.TreeNodeType;
  8 
  9 import java.lang.reflect.Field;
 10 import java.util.ArrayList;
 11 import java.util.List;
 12 
 13 /**
 14  * Created by keranbin on 2016/8/30.
 15  */
 16 public class TreeHelp {
 17     /**
 18      * 將服務器端取回的數據轉化成Node
 19      * @param datas
 20      * @return
 21      * @throws IllegalAccessError
 22      */
 23     public static <T> List<Node> convertDataToNodes(List<T> datas) throws IllegalAccessException {
 24         List<Node> nodes = new ArrayList<>();
 25         Node node = null;
 26         for (T t : datas) {
 27             String id = "";
 28             String pid = "";
 29             String label = null;
 30             node = null;
 31             Class c = t.getClass();
 32             Field fields[] = c.getDeclaredFields();
 33 
 34             for (Field field : fields) {
 35                 if (field.getAnnotation(TreeNodeId.class) != null) {
 36                     //設置訪問權限,強制性的可以訪問
 37                     field.setAccessible(true);
 38                     id= (String) field.get(t);
 39                 }
 40 
 41                 if (field.getAnnotation(TreeNodePid.class) != null) {
 42                     //設置訪問權限,強制性的可以訪問
 43                     field.setAccessible(true);
 44                     pid= (String) field.get(t);
 45                 }
 46 
 47                 if (field.getAnnotation(TreeNodeLabel.class) != null) {
 48                     //設置訪問權限,強制性的可以訪問
 49                     field.setAccessible(true);
 50                     label = (String) field.get(t);
 51                 }
 52 
 53 
 54             }
 55 
 56 
 57             node = new Node(id, pid, label);
 58             nodes.add(node);
 59         }
 60 
 61 
 62         /**
 63          * 循環對比兩個Node,設置節點間關聯關系
 64          */
 65         for (int i = 0; i < nodes.size(); i++) {
 66             Node n = nodes.get(i);
 67             for (int j = i+1; j < nodes.size(); j++) {
 68                 Node m = nodes.get(j);
 69                 if (m.getId().equals( n.getPid())) {//m是n的父節點
 70                     m.getChildren().add(n);
 71                     n.setParent(m);
 72                 } else if (m.getPid().equals( n.getId())) {//n是m的父節點
 73                     n.getChildren().add(m);
 74                     m.setParent(n);
 75                 }
 76             }
 77         }
 78 
 79         /**
 80          * 設置節點的圖標
 81          */
 82         for (Node n : nodes) {
 83             setNodeIcon(n);
 84         }
 85         return nodes;
 86     }
 87 
 88     /**
 89      * 為節點設置圖標
 90      * 邏輯:(1)如果當前節點有孩子節點並且處於展開狀態,那么設置向下的圖標
 91      * (2)如果當前節點有孩子節點病區處於閉合狀態,那么設置向右的圖標
 92      * (3)如果當前節點沒有孩子節點,傳參-1,到時候判斷是-1,不設置圖標
 93      * @param n
 94      */
 95     private static void setNodeIcon(Node n) {
 96         if (n.getChildren().size() > 0 && n.isExpand()) {
 97             n.setIcon(R.mipmap.tree_ex);
 98         } else if (n.getChildren().size() > 0 && !n.isExpand()) {
 99             n.setIcon(R.mipmap.tree_ec);
100         } else {
101             n.setIcon(-1);
102         }
103     }
104 
105     /**
106      * 得到排序后的Nodes
107      * @param datas
108      * @param <T>
109      * @return
110      */
111     public static <T> List<Node> getSortedNodes(List<T> datas, int defaultExpandLevel) throws IllegalAccessException {
112         List<Node> result = new ArrayList<>();
113         List<Node> nodes = convertDataToNodes(datas);
114         //獲取樹的根節點
115         List<Node> rootNodes = getRootNodes(nodes);
116 
117         for (Node node : rootNodes) {
118             addNode(result, node, defaultExpandLevel, 1);
119         }
120         return result;
121     }
122 
123     /**
124      * 從所有節點中過濾出根節點
125      * @param nodes
126      * @return
127      */
128     private static List<Node> getRootNodes(List<Node> nodes) {
129         List<Node> root = new ArrayList<>();
130         for (Node node : nodes) {
131             if (node.isRoot()) {
132                 root.add(node);
133             }
134         }
135         return root;
136     }
137 
138     /**
139      * 把一個節點的所有孩子節點都放入result
140      * @param result
141      * @param node               當前節點
142      * @param defaultExpandLevel 默認初始化是展開幾層
143      * @param currentLevel       當前節點層級
144      */
145     private static void addNode(List<Node> result, Node node, int defaultExpandLevel, int currentLevel) {
146         result.add(node);
147         //如果默認展開層級大於或者當前節點層級,那么設置當前層級是展開的,否則設置是閉合的
148         if (defaultExpandLevel >= currentLevel){
149             node.setExpand(true);
150         }
151 
152         //如果當前節點已經是葉子節點,那么不需要做任何處理啦
153         if(node.isLeft()){
154             return;
155 
156         }else{
157             //如果當前節點不是葉子節點,遞歸循環遍歷不斷的添加子節點
158             for(int i=0;i<node.getChildren().size();i++){
159                 addNode(result,node.getChildren().get(i),defaultExpandLevel,currentLevel+1);
160             }
161         }
162     }
163 
164     /**
165      * 過濾出可見的節點
166      * @param nodes
167      * @return
168      */
169     public static List<Node> filterVisibleNodes(List<Node> nodes){
170         List<Node> visibleNodes=new ArrayList<>();
171         for (Node node:nodes){
172             //如果當前節點是根節點或者當前節點的父節點是展開的
173             if (node.isRoot()||node.isParentExpand()){
174                 setNodeIcon(node);
175                 visibleNodes.add(node);
176             }
177         }
178         return visibleNodes;
179     }
180 }

 再來看看我們的公共adapter

 1 package com.example.keranbin.testdemo.treeHelp;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.AdapterView;
 8 import android.widget.BaseAdapter;
 9 import android.widget.ListView;
10 
11 import java.util.List;
12 
13 /**
14  * Created by keranbin on 2016/8/30.
15  */
16 public abstract class TreeListViewAdapter<T> extends BaseAdapter implements AdapterView.OnItemClickListener {
17     protected Context context;
18     protected ListView listView;
19     protected List<Node> mAllNodes;
20     protected List<Node> mVisibleNodes;
21     protected LayoutInflater inflater;
22 
23     private OnTreeNodeClickListener onTreeNodeClickListener;
24     
25     public TreeListViewAdapter(Context context, ListView listView, List<T> datas, int defaultExpandLevel) throws IllegalAccessException {
26         this.context = context;
27         this.listView = listView;
28         mAllNodes = TreeHelp.getSortedNodes(datas,defaultExpandLevel);
29         mVisibleNodes = TreeHelp.filterVisibleNodes(mAllNodes);
30         inflater = LayoutInflater.from(context);
31 
32         listView.setOnItemClickListener(this);
33     }
34 
35     @Override
36     public int getCount() {
37         return mVisibleNodes.size();
38     }
39 
40     @Override
41     public Object getItem(int position) {
42         return mVisibleNodes.get(position);
43     }
44 
45     @Override
46     public long getItemId(int position) {
47         return position;
48     }
49 
50     @Override
51     public View getView(int position, View view, ViewGroup viewGroup) {
52         Node node=mVisibleNodes.get(position);
53         view=getConvertView(node,position,view,viewGroup);
54         //設置內邊距
55         view.setPadding(30*node.getLevel(),3,3,3);
56         return view;
57     }
58 
59 
60     /**
61      * 設置點擊展開或者收縮
62      *
63      * @param position
64      */
65     private void expandOrCollapse(int position) {
66         Node node = mVisibleNodes.get(position);
67         if (node != null) {
68             if (node.isLeft())
69                 return;
70             node.setExpand(!node.isExpand());
71             mVisibleNodes=TreeHelp.filterVisibleNodes(mAllNodes);
72             notifyDataSetChanged();
73         }
74     }
75 
76     @Override
77     public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
78         expandOrCollapse(position);
79         if (onTreeNodeClickListener!=null){
80             onTreeNodeClickListener.setOnClick(mVisibleNodes.get(position),position);
81         }
82     }
83     
84     public void setOnTreeNodeClickListener(OnTreeNodeClickListener onTredNodeClickListener){
85         this.onTreeNodeClickListener=onTredNodeClickListener;
86     }
87 
88     /**
89      * 設置node的點擊回調
90      */
91     public interface OnTreeNodeClickListener{
92         void setOnClick(Node node, int position);
93     }
94     
95     public abstract View getConvertView(Node node,int position, View view, ViewGroup viewGroup);
96 }

我們建一個CheckBoxTreeListViewAdapter繼承自TreeViewAdapter

  1 package com.example.keranbin.testdemo;
  2 
  3 import android.content.Context;
  4 import android.view.View;
  5 import android.view.ViewGroup;
  6 import android.widget.CheckBox;
  7 import android.widget.CompoundButton;
  8 import android.widget.ImageView;
  9 import android.widget.ListView;
 10 import android.widget.TextView;
 11 
 12 import com.example.keranbin.testdemo.treeHelp.Node;
 13 import com.example.keranbin.testdemo.treeHelp.TreeListViewAdapter;
 14 
 15 import java.util.ArrayList;
 16 import java.util.List;
 17 
 18 /**
 19  * Created by keranbin on 2016/8/30.
 20  */
 21 public class CheckBoxTreeListViewAdapter<T> extends TreeListViewAdapter {
 22     //判斷checkbox是否是單選的標志
 23     private boolean isSingle = true;
 24 
 25     private static List<Node> nodeList;
 26     
 27     private OnTreeNodeChooseListener onTreeNodeChooseListener;
 28 
 29     /**
 30      * @param context           上下文對象
 31      * @param listView              
 32      * @param datas             
 33      * @param defaultExpandLevel  默認初始化時展開幾層
 34      * @param isSingle              checkbox是不是單選的
 35      * @throws IllegalAccessException
 36      */
 37     public CheckBoxTreeListViewAdapter(Context context, ListView listView, List<T> datas, int defaultExpandLevel, boolean isSingle) throws IllegalAccessException {
 38         super(context, listView, datas, defaultExpandLevel);
 39         this.isSingle = isSingle;
 40     }
 41 
 42     
 43     @Override
 44     public View getConvertView(final com.example.keranbin.testdemo.treeHelp.Node node, int position, View view, ViewGroup viewGroup) {
 45         ViewHolder vh=null;
 46         if (view == null) {
 47             vh = new ViewHolder();
 48             view = inflater.inflate(R.layout.lv_tree_item, viewGroup, false);
 49             vh.ivIcon = (ImageView) view.findViewById(R.id.iv_tree_icon);
 50             vh.tvName = (TextView) view.findViewById(R.id.tv_tree_title);
 51             vh.cbChoose = (CheckBox) view.findViewById(R.id.cb_tree_choose);
 52             view.setTag(vh);
 53         } else {
 54             vh = (ViewHolder) view.getTag();
 55         }
 56 
 57 
 58         //如果node的icon為-1,說明是葉子節點,隱藏圖標,顯示checkbox,否則顯示相應的圖標,隱藏checkbox
 59         if (node.getIcon() == -1) {
 60             vh.ivIcon.setVisibility(View.INVISIBLE);
 61             vh.cbChoose.setVisibility(View.VISIBLE);
 62             vh.cbChoose.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
 63                 @Override
 64                 public void onCheckedChanged(CompoundButton compoundButton, boolean isCheck) {
 65                     if (isSingle) {  //如果checkbox是單選的
 66                         if (isCheck) { //如果checkbox的狀態是選中的,那么除了被選中的那條數據,其他Node節點的checkbox狀態都為false
 67                             for (int i = 0; i < mAllNodes.size(); i++) {
 68                                 if (((Node) mAllNodes.get(i)).getId().equals(node.getId())) {
 69                                     ((Node) mAllNodes.get(i)).setChoose(isCheck);
 70                                 } else {
 71                                     ((Node) mAllNodes.get(i)).setChoose(false);
 72                                 }
 73                             }
 74                         } else {//如果checkbox的狀態是選中的,所有Node節點checkbox狀態都為false
 75                             for (int i = 0; i < mAllNodes.size(); i++) {
 76                                 if (((Node) mAllNodes.get(i)).getId().equals(node.getId())) {
 77                                     ((Node) mAllNodes.get(i)).setChoose(isCheck);
 78                                 }
 79                             }
 80                         }
 81                     } else {   ////如果checkbox是多選的,對應node節點的checkbox狀態視用戶的操作而定
 82                         for (int i = 0; i < mAllNodes.size(); i++) {
 83                             if (((Node) mAllNodes.get(i)).getId().equals(node.getId()))
 84                                 ((Node) mAllNodes.get(i)).setChoose(isCheck);
 85 
 86                         }
 87                     }
 88                     onTreeNodeChooseListener.OnTreeNodeChoose(getSelectedNodes());//回調所選擇的節點數據給用戶
 89                     notifyDataSetChanged();
 90                 }
 91             });
 92             vh.cbChoose.setChecked(node.isChoose());
 93         } else {
 94             vh.ivIcon.setVisibility(View.VISIBLE);
 95             vh.ivIcon.setImageResource(node.getIcon());
 96             vh.cbChoose.setVisibility(View.INVISIBLE);
 97         }
 98         vh.tvName.setText(node.getName());
 99         return view;
100     }
101     
102     /**
103      * 返回所選node集合
104      * @return
105      */
106     public List<Node> getSelectedNodes(){
107         nodeList=new ArrayList<>();
108         for(int i=0;i<mAllNodes.size();i++){
109             if(((Node)mAllNodes.get(i)).isChoose()){
110                 nodeList.add((Node) mAllNodes.get(i));
111             }
112         }
113         return nodeList;
114     }
115 
116     public void setOnTreedNodeChooseListener(OnTreeNodeChooseListener onTreeNodeChooseListener) {
117         this.onTreeNodeChooseListener = onTreeNodeChooseListener;
118     }
119     
120     public interface OnTreeNodeChooseListener {
121         void OnTreeNodeChoose(List<Node> nodes);
122     }
123 
124     class ViewHolder {
125         private ImageView ivIcon;
126         private TextView tvName;
127         private CheckBox cbChoose;
128     }
129 }

至此,我們的程序結束了嗎,沒有,看看下面出現的這種情況

細心的童鞋就會發現原生動物不是具體的動物,居然可以選擇???????這是什么情況造成的呢,是我們代碼的問題,是的,我們的代碼邏輯還不夠嚴謹,造成這種情況的原因是可能管理員還沒為原始動物添加具體的動物,而我們代碼中,原始動物並沒有子節點,是葉子節點,是可以選擇的,到底怎么解決?無非就是添加一個字段進行判斷是不是具體的動物,具體實現就不啰嗦,大家可以自己研究研究。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM