公司项目中有一个选择联系人的界面,一看里面关系极其复杂,最多时有5层关系嵌套,层数还不一定,有的是第五级是人员,有的是第四级是人员,崩溃中……原来的实现方式是分了三个Activity去分别加载,个人觉得太过臃肿麻烦,选个人要调四次页面,太繁琐了。就想能不能把它整到一个页面中去,既能全选所有人又能实现单选几个人。
刚开始尝试着用 ExpandableListView 实现,效果是实现了但全选状态传递不好弄,如何点击某一层节点,让它的所有孩子都选中?整了半天没整好。心想这轮子肯定有人造过了,本着不重复造轮子的理念,去网上找找看吧。
看了 n 篇文章后,总结一下就是两种解决方案,一种是用 ExpandableListView 实现,还没有见到有案例实现全选的。另一种是直接用 ListView 实现 n 级嵌套,还能全选全不选!就第二种了。
用 ListView 实现的文章几乎所有案例都参照了 鸿神 的那篇 Android 打造任意层级树形控件 考验你的数据结构和设计, 默默的献上膝盖~
鸿神的这篇文章大概看了下,因为后面有很多人都在这个基础上做的优化,原理也写的很详细,索性直接研究后者吧。几经筛选最终挑出了这篇:更快实现Android多级树形选择列表
借鉴了上面的经验之后,顺利的做了出来。
先上图:
分析
实现原理我就不多说啦,上面两篇文章里都讲的很清楚了。简单来讲就是把所有节点都当成一个 Node 对象,Node 对象里有该节点的 id, 它的父节点的 id:pId, 它所有子节点的 list 集合: children, 该节点的层级 level 等等。在设置数据的时候就对数据进行处理,把层级关系排好,按层级依次显示。当选中某个节点时,将它的父节点设置为选中,再将它的所有子节点循环一遍都设置为选中,就解决了全选问题。
用法
本案例中我将选择联系人的操作封装到了一个 Activity 里面,用的时候很简单,只需启动 Activity 的时候传两个参数就可以:
Intent intent = new Intent(this, SelectReceiverActivity.class);
//要请求的数据类型,将项目中用到的类型都封装到枚举类 ReceiverType 里
intent.putExtra("type", ReceiverType.TEACHER);
//本次请求的标记,用于当一个页面要多次调用选人界面时,拿到选择结果的时候做区分
intent.putExtra("flag", "record");
startActivity(intent);
选择的结果通过 EventBus 传递,不了解 EventBus 的请自行补习~
EventBus 的接收事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(SelectReceiverEvent event){
String flag = event.getFlag();
/* 如果没传flag可省去判空操作,也可省去判断flag的操作
直接根据需要对数据进行处理 */
if(flag == null){
return;
}
if(TextUtils.equals("flag1", flag)){
//你自己的操作
}else if(TextUtils.equals("flag2", flag)){
//你自己的操作
}
}
实现
温馨提示:下面的代码可直接复制到你的项目中,根据需要进行删改。手把手教你怎么快速集成到项目中。
先偷个懒,为了能直接用 zhangke3016
的四个工具类,先建个跟他项目一样的包:com.multilevel.treelist
,然后将 Node.java
, OnTreeNodeClickListener.java
, TreeHelper.java
, TreeListViewAdapter.java
四个类直接拷到这个包下。
上代码
工具类篇
跟业务关系不大,可直接拷贝
Node.java
这是节点对象,里面封装了能用到的所有信息,是多级列表的核心类
package com.multilevel.treelist;
import java.util.ArrayList;
import java.util.List;
public class Node<T,B> {
/**
* 传入的实体对象
*/
public B bean;
/**
* 设置开启 关闭的图片
*/
public int iconExpand=-1, iconNoExpand = -1;
private T id;
/**
* 根节点pId为0
*/
private T pId ;
private String name;
/**
* 当前的级别
*/
private int level;
/**
* 是否展开
*/
private boolean isExpand = false;
private int icon = -1;
/**
* 下一级的子Node
*/
private List<Node> children = new ArrayList<>();
/**
* 父Node
*/
private Node parent;
/**
* 是否被checked选中
*/
private boolean isChecked;
/**
* 是否为新添加的
*/
public boolean isNewAdd = true;
/**
* 该分组下的人数
*/
private int count;
/**
* 是否是人,1=true, 0=false
*/
private int isPeople;
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
}
public Node() {}
public Node(T id, T pId, String name) {
super();
this.id = id;
this.pId = pId;
this.name = name;
}
public Node(T id, T pId, String name, B bean) {
super();
this.id = id;
this.pId = pId;
this.name = name;
this.bean = bean;
}
public Node(T id, T pId, String name, int count) {
this.id = id;
this.pId = pId;
this.name = name;
this.count = count;
}
public Node(T id, T pId, String name, int count, int isPeople) {
this.id = id;
this.pId = pId;
this.name = name;
this.count = count;
this.isPeople = isPeople;
}
public int getIcon()
{
return icon;
}
public void setIcon(int icon)
{
this.icon = icon;
}
public T getId()
{
return id;
}
public void setId(T id)
{
this.id = id;
}
public T getpId()
{
return pId;
}
public void setpId(T pId)
{
this.pId = pId;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public void setLevel(int level) {
this.level = level;
}
public boolean isExpand() {
return isExpand;
}
public List<Node> getChildren() {
return children;
}
public void setChildren(List<Node> children) {
this.children = children;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int getIsPeople() {
return isPeople;
}
public void setIsPeople(int isPeople) {
this.isPeople = isPeople;
}
/**
* 是否为跟节点
*
* @return
*/
public boolean isRoot() {
return parent == null;
}
/**
* 判断父节点是否展开
*
* @return
*/
public boolean isParentExpand() {
if (parent == null)
return false;
return parent.isExpand();
}
/**
* 是否是叶子界点
*
* @return
*/
public boolean isLeaf()
{
return children.size() == 0;
}
/**
* 获取level
*/
public int getLevel() {
return parent == null ? 0 : parent.getLevel() + 1;
}
/**
* 设置展开
*
* @param isExpand
*/
public void setExpand(boolean isExpand) {
this.isExpand = isExpand;
if (!isExpand) {
for (Node node : children) {
node.setExpand(isExpand);
}
}
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", pId=&#