花了几天研究了下鸿扬大神的博客《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 }
至此,我们的程序结束了吗,没有,看看下面出现的这种情况
细心的童鞋就会发现原生动物不是具体的动物,居然可以选择???????这是什么情况造成的呢,是我们代码的问题,是的,我们的代码逻辑还不够严谨,造成这种情况的原因是可能管理员还没为原始动物添加具体的动物,而我们代码中,原始动物并没有子节点,是叶子节点,是可以选择的,到底怎么解决?无非就是添加一个字段进行判断是不是具体的动物,具体实现就不啰嗦,大家可以自己研究研究。