Android充电站安卓Android开发

Android多级树形选择列表案例 - 手把手教你快速实现

2017-05-22  本文已影响7849人  86cf4d336efc

公司项目中有一个选择联系人的界面,一看里面关系极其复杂,最多时有5层关系嵌套,层数还不一定,有的是第五级是人员,有的是第四级是人员,崩溃中……原来的实现方式是分了三个Activity去分别加载,个人觉得太过臃肿麻烦,选个人要调四次页面,太繁琐了。就想能不能把它整到一个页面中去,既能全选所有人又能实现单选几个人。

刚开始尝试着用 ExpandableListView 实现,效果是实现了但全选状态传递不好弄,如何点击某一层节点,让它的所有孩子都选中?整了半天没整好。心想这轮子肯定有人造过了,本着不重复造轮子的理念,去网上找找看吧。

看了 n 篇文章后,总结一下就是两种解决方案,一种是用 ExpandableListView 实现,还没有见到有案例实现全选的。另一种是直接用 ListView 实现 n 级嵌套,还能全选全不选!就第二种了。

用 ListView 实现的文章几乎所有案例都参照了 鸿神 的那篇 Android 打造任意层级树形控件 考验你的数据结构和设计, 默默的献上膝盖~

鸿神的这篇文章大概看了下,因为后面有很多人都在这个基础上做的优化,原理也写的很详细,索性直接研究后者吧。几经筛选最终挑出了这篇:更快实现Android多级树形选择列表
借鉴了上面的经验之后,顺利的做了出来。

先上图:

选择联系人.gif

分析

实现原理我就不多说啦,上面两篇文章里都讲的很清楚了。简单来讲就是把所有节点都当成一个 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 四个类直接拷到这个包下。

目录结构.png

上代码

工具类篇

跟业务关系不大,可直接拷贝

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=" + pId +
                ", name='" + name + '\'' +
                ", level=" + level +
                ", isPeople=" + isPeople +
                ", count=" + count +
                '}';
    }
}

TreeHelper.java
节点对象 Node 的工具类,对 Node 相关操作的封装

package com.multilevel.treelist;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

public class TreeHelper {

    /**
     * 传入node  返回排序后的Node
     *
     * @param datas
     * @param defaultExpandLevel
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static List<Node> getSortedNodes(List<Node> datas,
                                            int defaultExpandLevel) {
        List<Node> result = new ArrayList<Node>();
        // 设置Node间父子关系
        List<Node> nodes = convetData2Node(datas);
        // 拿到根节点
        List<Node> rootNodes = getRootNodes(nodes);
        // 排序以及设置Node间关系
        for (Node node : rootNodes) {
            addNode(result, node, defaultExpandLevel, 1);
        }
        return result;
    }

    /**
     * 过滤出所有可见的Node
     *
     * @param nodes
     * @return
     */
    public static List<Node> filterVisibleNode(List<Node> nodes) {
        List<Node> result = new ArrayList<Node>();

        for (Node node : nodes) {
            // 如果为跟节点,或者上层目录为展开状态
            if (node.isRoot() || node.isParentExpand()) {
                setNodeIcon(node);
                result.add(node);
            }
        }
        return result;
    }

    /**
     * 设置Node间,父子关系;让每两个节点都比较一次,即可设置其中的关系
     */
    private static List<Node> convetData2Node(List<Node> nodes) {

        for (int i = 0; i < nodes.size(); i++) {
            Node n = nodes.get(i);
            for (int j = i + 1; j < nodes.size(); j++) {
                Node m = nodes.get(j);
                    if (m.getpId() == n.getId()) {
                        n.getChildren().add(m);
                        m.setParent(n);
                    } else if (m.getId() == n.getpId()) {
                        m.getChildren().add(n);
                        n.setParent(m);
                    }
            }
        }

        return nodes;
    }

    private static List<Node> getRootNodes(List<Node> nodes) {
        List<Node> root = new ArrayList<Node>();
        for (Node node : nodes) {
            if (node.isRoot())
                root.add(node);
        }
        return root;
    }

    /**
     * 把一个节点上的所有的内容都挂上去
     */
    private static <T,B> void addNode(List<Node> nodes, Node<T,B> node,
                                      int defaultExpandLeval, int currentLevel) {
        nodes.add(node);

        if (node.isNewAdd && defaultExpandLeval >= currentLevel) {
            node.setExpand(true);
        }

        if (node.isLeaf())
            return;
        for (int i = 0; i < node.getChildren().size(); i++) {
            addNode(nodes, node.getChildren().get(i), defaultExpandLeval,
                    currentLevel + 1);
        }
    }

    /**
     * 设置节点的图标
     *
     * @param node
     */
    private static void setNodeIcon(Node node) {
        if (node.getChildren().size() > 0 && node.isExpand()) {
            node.setIcon(node.iconExpand);
        } else if (node.getChildren().size() > 0 && !node.isExpand()) {
            node.setIcon(node.iconNoExpand);
        } else {
            node.setIcon(-1);
        }
    }
}

TreeListViewAdapter.java
对 ListView 的 Adapter 的封装

package com.multilevel.treelist;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;

public abstract class TreeListViewAdapter extends BaseAdapter {

    protected Context mContext;
    /**
     * 存储所有可见的Node
     */
    protected List<Node> mNodes = new ArrayList<>();
    protected LayoutInflater mInflater;

    private List<Node> mDatas = new ArrayList<>();

    private ListView mTree;

    /**
     * 存储所有的Node
     */
    protected List<Node> mAllNodes = new ArrayList<>();

    /**
     * 点击的回调接口
     */
    private OnTreeNodeClickListener onTreeNodeClickListener;
    /**
     * 默认不展开
     */
    private int defaultExpandLevel = 0;
    /**
     * 展开与关闭的图片
     */
    private int iconExpand = -1, iconNoExpand = -1;

    public void setOnTreeNodeClickListener(
            OnTreeNodeClickListener onTreeNodeClickListener) {
        this.onTreeNodeClickListener = onTreeNodeClickListener;
    }

    public TreeListViewAdapter(ListView mTree, Context context, List<Node> datas,
                               int defaultExpandLevel, int iconExpand, int iconNoExpand) {

        this.iconExpand = iconExpand;
        this.iconNoExpand = iconNoExpand;
        this.mDatas = datas;
        this.defaultExpandLevel = defaultExpandLevel;
        mContext = context;
        this.mTree = mTree;

        initData();
    }

    private void initData() {
        for (Node node : mDatas) {
            node.getChildren().clear();
            node.iconExpand = iconExpand;
            node.iconNoExpand = iconNoExpand;
        }

        /**
         * 对所有的Node进行排序
         */
        mAllNodes = TreeHelper.getSortedNodes(mDatas, defaultExpandLevel);
        /**
         * 过滤出可见的Node
         */
        mNodes = TreeHelper.filterVisibleNode(mAllNodes);
        mInflater = LayoutInflater.from(mContext);
        /**
         * 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
         */
        mTree.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                expandOrCollapse(position);

                if (onTreeNodeClickListener != null) {
                    onTreeNodeClickListener.onClick(mNodes.get(position),
                            position);
                }
            }

        });
    }

    /**
     * @param mTree
     * @param context
     * @param datas
     * @param defaultExpandLevel 默认展开几级树
     */
    public TreeListViewAdapter(ListView mTree, Context context, List<Node> datas,
                               int defaultExpandLevel) {
        this(mTree, context, datas, defaultExpandLevel, -1, -1);
    }

    /**
     * 清除掉之前数据并刷新  重新添加
     *
     * @param mlists
     * @param defaultExpandLevel 默认展开几级列表
     */
    public void addDataAll(List<Node> mlists, int defaultExpandLevel) {
        mAllNodes.clear();
        addData(-1, mlists, defaultExpandLevel);
    }

    /**
     * 在指定位置添加数据并刷新 可指定刷新后显示层级
     *
     * @param index
     * @param mlists
     * @param defaultExpandLevel 默认展开几级列表
     */
    public void addData(int index, List<Node> mlists, int defaultExpandLevel) {
        this.defaultExpandLevel = defaultExpandLevel;
        notifyData(index, mlists);
    }

    /**
     * 在指定位置添加数据并刷新
     *
     * @param index
     * @param mlists
     */
    public void addData(int index, List<Node> mlists) {
        notifyData(index, mlists);
    }

    /**
     * 添加数据并刷新
     *
     * @param mlists
     */
    public void addData(List<Node> mlists) {
        addData(mlists, defaultExpandLevel);
    }

    /**
     * 添加数据并刷新 可指定刷新后显示层级
     *
     * @param mlists
     * @param defaultExpandLevel
     */
    public void addData(List<Node> mlists, int defaultExpandLevel) {
        this.defaultExpandLevel = defaultExpandLevel;
        notifyData(-1, mlists);
    }

    /**
     * 添加数据并刷新
     *
     * @param node
     */
    public void addData(Node node) {
        addData(node, defaultExpandLevel);
    }

    /**
     * 添加数据并刷新 可指定刷新后显示层级
     *
     * @param node
     * @param defaultExpandLevel
     */
    public void addData(Node node, int defaultExpandLevel) {
        List<Node> nodes = new ArrayList<>();
        nodes.add(node);
        this.defaultExpandLevel = defaultExpandLevel;
        notifyData(-1, nodes);
    }

    /**
     * 刷新数据
     *
     * @param index
     * @param mListNodes
     */
    private void notifyData(int index, List<Node> mListNodes) {
        for (int i = 0; i < mListNodes.size(); i++) {
            Node node = mListNodes.get(i);
            node.getChildren().clear();
            node.iconExpand = iconExpand;
            node.iconNoExpand = iconNoExpand;
        }
        for (int i = 0; i < mAllNodes.size(); i++) {
            Node node = mAllNodes.get(i);
            node.getChildren().clear();
            node.isNewAdd = false;
        }
        if (index != -1) {
            mAllNodes.addAll(index, mListNodes);
        } else {
            mAllNodes.addAll(mListNodes);
        }
        /**
         * 对所有的Node进行排序
         */
        mAllNodes = TreeHelper.getSortedNodes(mAllNodes, defaultExpandLevel);
        /**
         * 过滤出可见的Node
         */
        mNodes = TreeHelper.filterVisibleNode(mAllNodes);
        //刷新数据
        notifyDataSetChanged();
    }

    /**
     * 获取排序后所有节点
     *
     * @return
     */
    public List<Node> getAllNodes() {
        if (mAllNodes == null)
            mAllNodes = new ArrayList<Node>();
        return mAllNodes;
    }

    /**
     * 相应ListView的点击事件 展开或关闭某节点
     *
     * @param position
     */
    public void expandOrCollapse(int position) {
        Node n = mNodes.get(position);

        if (n != null) {// 排除传入参数错误异常
            if (!n.isLeaf()) {
                n.setExpand(!n.isExpand());
                mNodes = TreeHelper.filterVisibleNode(mAllNodes);
                notifyDataSetChanged();// 刷新视图
            }
        }
    }

    @Override
    public int getCount() {
        return mNodes.size();
    }

    @Override
    public Object getItem(int position) {
        return mNodes.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Node node = mNodes.get(position);
        convertView = getConvertView(node, position, convertView, parent);
        // 设置内边距
        convertView.setPadding(node.getLevel() * CommonUtils.dp2px(mContext, 20), 3, 3, 3);
        return convertView;
    }

    /**
     * 设置多选
     *
     * @param node
     * @param checked
     */
    protected void setChecked(final Node node, boolean checked) {
        node.setChecked(checked);
        setChildChecked(node, checked);
        if (node.getParent() != null)
            setNodeParentChecked(node.getParent(), checked);
        notifyDataSetChanged();
    }

    /**
     * 设置是否选中
     *
     * @param node
     * @param checked
     */
    public <T, B> void setChildChecked(Node<T, B> node, boolean checked) {
        if (!node.isLeaf()) {
            node.setChecked(checked);
            for (Node childrenNode : node.getChildren()) {
                setChildChecked(childrenNode, checked);
            }
        } else {
            node.setChecked(checked);
        }
    }

    private void setNodeParentChecked(Node node, boolean checked) {
        if (checked) {
            node.setChecked(checked);
            if (node.getParent() != null)
                setNodeParentChecked(node.getParent(), checked);
        } else {
            List<Node> childrens = node.getChildren();
            boolean isChecked = false;
            for (Node children : childrens) {
                if (children.isChecked()) {
                    isChecked = true;
                }
            }
            //如果所有自节点都没有被选中 父节点也不选中
            if (!isChecked) {
                node.setChecked(checked);
            }
            if (node.getParent() != null)
                setNodeParentChecked(node.getParent(), checked);
        }
    }

    public abstract View getConvertView(Node node, int position,
                                        View convertView, ViewGroup parent);

    public void setData(List<Node> datas) {
        this.mDatas = datas;
        initData();
    }

}

OnTreeNodeClickListener.java
监听节点点击的监听器

package com.multilevel.treelist;

public interface OnTreeNodeClickListener {
    void onClick(Node node, int position);
}

将以上四个类拷贝进项目中去,算是工具类,一般情况下这几个类不用变,Node 的属性可根据需要增删。下面就是项目的具体实现了:

具体业务实现篇

下面跟业务强相关,需要根据自己的项目做相应修改

SelectReceiverActivity.java
实现多级菜单的最终封装体,可直接被调用

/**
 * 选择联系人的Activity
 * 用法:直接启动该Activity,intent传参数 "type",值为枚举值ReceiverType
 * flag 参数可传可不传,是用来标记是谁请求的。主要用于一个页面请求多次来做区分
 * 传值用EventBus。在需要接受选人结果的页面注册EventBus,并接收事件SelectReceiverEvent
 * SelectReceiverEvent事件封装了 选择的人数count 和 选择的人的id拼成的字符串
 */

public class SelectReceiverActivity extends BaseActivity {

    private ListView lv;
    private List<Node> mDatas = new ArrayList<>();

    private static final String ALL = "all";
    private static final String TEACHER = "teacher";
    private static final String STUDENT = "student";
    private static final String PARENT = "parent";
    private ReceiverSelectService service;
    private String token;
    private CloseableRxServiceExecutor executor;
    private Context context;
    private SimpleTreeAdapter mAdapter;
    private CustomProgressDialog progressDialog;
    private CommonTitleView titleView;

    private int studentTotal;  //学生总人数
    private int parentTotal;  //家长总人数
    private int teacherTotal;  //老师总人数
    private Intent intent;
    private String userTypes = "";
    private String flag;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_select_receiver2);

        context = MyApplication.getContext();
        initUserTypes();
        initView();
        initData();
    }

    private void initUserTypes() {
        Intent intent = getIntent();
        //flag用来标记是谁启动了它,如果flag不为空,Event将flag返回,作为区别的标识
        flag = intent.getStringExtra("flag");
        ReceiverType type = (ReceiverType) intent.getSerializableExtra("type");
        //自己的逻辑。根据传的 type 请求不同的数据

    }

    private void initView() {
        lv = (ListView) findViewById(R.id.lv);
        titleView = (CommonTitleView) findViewById(R.id.title_view);

    }

    private void initData() {
        initTitle();
        
        //网络请求的服务类,用的 retrofit 框架
        service = ApiManager.getReceiverSelectService();
        //RxJava 封装的工具类
        executor = new CloseableRxServiceExecutor();
        //当前用户的token
        token = StringUtils.getToken(this);

        //加载中进度条
        progressDialog = CustomProgressDialog.create(this, CommonUtils.getResString(R.string.load_data));
        progressDialog.show();

        //Retrofit + RxJava 网络请求
        Single<DataModel> single = service.getAppUser(token, userTypes);
        executor.execute(single, new Action1<DataModel>() {
            @Override
            public void call(DataModel dataModel) {
                setListData(dataModel);
            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                progressDialog.dismiss();
                ToastUtils.showToast(CommonUtils.getResString(R.string.request_data_error));
            }
        });
    }

    /**
     * 解析服务器返回的数据。让服务器把所有的人都包装到json里返回了
     * 所以拿到数据后要拆分开
     * 这里要替换为你自己的逻辑
     */
    private void setListData(DataModel dataModel) {
        List<GradeModel> studentData = dataModel.getStudent();
        List<GradeModel> parentData = dataModel.getParent();
        List<GradeModel> teacherData = dataModel.getTeacher();

        if (studentData != null && studentData.size() != 0) {
            //遍历学生
            for (GradeModel model : studentData) {
                studentTotal += model.getCount();
                //将年级数据封装进集合,第三级菜单
                mDatas.add(new Node(model.getId(), STUDENT, model.getName(), model.getCount(), model.getIsPeople()));
                //遍历班级
                List<ClassModel> classList = model.getList();
                if (classList != null && classList.size() != 0) {
                    for (ClassModel classModel : classList) {
                        String classId = classModel.getId();
                        //将班级数据封装进集合,第四级菜单
                        mDatas.add(new Node(classId, model.getId(), classModel.getName(), classModel.getCount(), classModel.getIsPeople()));
                        //遍历人员
                        List<StudentModel> studentList = classModel.getList();
                        if (studentList != null && studentList.size() != 0) {
                            for (StudentModel stuModel : studentList) {
                                //将人员数据封装进集合,第五级菜单
                                Node stuNode = new Node(stuModel.getId(), classId, stuModel.getName(), 0, stuModel.getIsPeople());
                                mDatas.add(stuNode);
                            }
                        }
                    }
                }
            }
            //创建第二级菜单
            mDatas.add(new Node(STUDENT, ALL, "学生", studentTotal));
        }

        if (parentData != null && parentData.size() != 0) {
            //遍历家长
            for (GradeModel model : parentData) {
                parentTotal += model.getCount();
                mDatas.add(new Node(model.getId(), PARENT, model.getName(), model.getCount(), model.getIsPeople()));
                //遍历班级
                List<ClassModel> classList = model.getList();
                if (classList != null && classList.size() != 0) {
                    for (ClassModel classModel : classList) {
                        String classId = classModel.getId();
                        mDatas.add(new Node(classId, model.getId(), classModel.getName(), classModel.getCount(), classModel.getIsPeople()));
                        //遍历人员
                        List<StudentModel> studentList = classModel.getList();
                        if (studentList != null && studentList.size() != 0) {
                            for (StudentModel stuModel : studentList) {
                                Node stuNode = new Node(stuModel.getId(), classId, stuModel.getName(), 0, stuModel.getIsPeople());
                                mDatas.add(stuNode);
                            }
                        }
                    }
                }
            }
            mDatas.add(new Node(PARENT, ALL, "家长", parentTotal));
        }

        if (teacherData != null && teacherData.size() != 0) {
            //遍历老师
            for (GradeModel model : teacherData) {
                teacherTotal += model.getCount();
                mDatas.add(new Node(model.getId(), TEACHER, model.getName(), model.getCount(), model.getIsPeople()));
                //遍历班级
                List<ClassModel> classList = model.getList();
                if (classList != null && classList.size() != 0) {
                    for (ClassModel classModel : classList) {
                        String classId = classModel.getId();
                        mDatas.add(new Node(classId, model.getId(), classModel.getName(), classModel.getCount(), classModel.getIsPeople()));
                        //遍历人员
                        List<StudentModel> studentList = classModel.getList();
                        if (studentList != null && studentList.size() != 0) {
                            for (StudentModel stuModel : studentList) {
                                Node stuNode = new Node(stuModel.getId(), classId, stuModel.getName(), 0, stuModel.getIsPeople());
                                mDatas.add(stuNode);
                            }
                        }
                    }
                }
            }
            mDatas.add(new Node(TEACHER, ALL, "教师", teacherTotal));
        }

        progressDialog.dismiss();

        //所有人数
        int allTotal = parentTotal + studentTotal + teacherTotal;
        //创建第一级菜单
        mDatas.add(new Node(ALL, "-1", "全体人员", allTotal));
        
        //给 ListView 设置 Adapter
        mAdapter = new SimpleTreeAdapter(lv, context, mDatas, 1, R.drawable.icon_sub, R.drawable.icon_add);
        lv.setAdapter(mAdapter);

    }

    private void initTitle() {
        titleView.setTitleShowType(CommonTitleView.ShowType.MIDDLE_TEXT_AND_RIGHT_TEXT);
        titleView.setMiddleText("选择接收人");
        titleView.setRightText("确定");
        titleView.setOnBackButtonClick(new CommonTitleView.OnBackButtonClick() {
            @Override
            public void onBackClick() {
                finish();
            }
        });

        //点击确定按钮
        titleView.setOnRightTextClick(new CommonTitleView.OnRightTextClick() {
            @Override
            public void onTextClick() {

                if (mAdapter == null) {
                    return;
                }

                List<Node> allNodes = mAdapter.getAllNodes();
                List<String> selectNodesId = new ArrayList<String>();
                for (Node node : allNodes) {
                    if (node.isChecked() && (node.getIsPeople() == 1)) {
                        selectNodesId.add(node.getId().toString());
                    }
                }

                //去重操作
                List list = CommonUtils.deleteRepeat(selectNodesId);
                //格式化
                String ids = list.toString();
                String substring = ids.substring(1, ids.length() - 1);
                String result = substring.replace(", ", ",");

                //用EventBus发送选中的ids 和 一共选了多少人
                if (flag == null) {
                    EventBus.getDefault().post(new SelectReceiverEvent(list.size(), result));
                } else {
                    //flag不为空,返回
                    EventBus.getDefault().post(new SelectReceiverEvent(list.size(), result, flag));
                }

                finish();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //取消请求
        try {
            executor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //关闭加载中进度条
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }
}

SimpleTreeAdapter
自定义的Adapter,继承了 TreeListViewAdapter

public class SimpleTreeAdapter extends TreeListViewAdapter {

    private Context context;

    public SimpleTreeAdapter(ListView mTree, Context context, List<Node> datas, int defaultExpandLevel, int iconExpand, int iconNoExpand) {
        super(mTree, context, datas, defaultExpandLevel, iconExpand, iconNoExpand);
        this.context = context;
    }

    @Override
    public View getConvertView(final Node node, int position, View convertView, ViewGroup parent) {
        View view;
        final ViewHolder holder;
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.item_receiver_select_title, null);
            holder = new ViewHolder();
            holder.tvName = (TextView) view.findViewById(R.id.tv_name);
            holder.tvCount = (TextView) view.findViewById(R.id.tv_count);
            holder.cb = (CheckBox) view.findViewById(R.id.check_box);
            holder.iv = (ImageView) view.findViewById(R.id.iv_right);
            view.setTag(holder);
        } else {
            view = convertView;
            holder = (ViewHolder) view.getTag();
        }

        holder.cb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setChecked(node, holder.cb.isChecked());
            }
        });

        if (node.isChecked()){
            holder.cb.setChecked(true);
        }else {
            holder.cb.setChecked(false);
        }

        if (node.getIcon() == -1) {
            holder.iv.setVisibility(View.INVISIBLE);
        } else {
            holder.iv.setVisibility(View.VISIBLE);
            holder.iv.setImageResource(node.getIcon());
        }

        holder.tvName.setText(node.getName());

        //不是学生,都显示当前分类下有多少人
        if (node.getIsPeople() != 1) {
            holder.tvCount.setVisibility(View.VISIBLE);
            holder.tvCount.setText("(" + node.getCount() + ")");
        } else {
            holder.tvCount.setVisibility(View.GONE);
        }

        return view;
    }

    class ViewHolder {
        TextView tvName, tvCount;
        CheckBox cb;
        ImageView iv;
    }
}

资源文件 item_receiver_select_title.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/selector_list_item_bg_gray2gray"
    android:descendantFocusability="blocksDescendants"
    android:orientation="horizontal">

    <CheckBox
        android:id="@+id/check_box"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="5dp"
        android:button="@drawable/selector_checkbox" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingBottom="10dp"
        android:paddingTop="10dp">

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:gravity="center_vertical"
            android:text="项目"
            android:textColor="@color/black"
            android:textSize="15sp" />

        <TextView
            android:id="@+id/tv_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:gravity="center_vertical"
            android:text="(20)"
            android:textColor="@color/elec_file_gray_bg"
            android:textSize="12sp"
            android:visibility="gone" />

    </LinearLayout>
    
    <ImageView
        android:id="@+id/iv_right"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_gravity="center"
        android:layout_marginRight="5dp"
        android:src="@drawable/icon_add" />

</LinearLayout>

附上 list 去重方法

/**
 * 去除list集合中的重复元素
 * @param list 要去重的 list
 * @return 返回去重后的 list
 */
public static List deleteRepeat(List list){
    List newList = new ArrayList<>();
    Set set = new HashSet();
    for (Object obj : list) {
        if(set.add(obj)){
            newList.add(obj);
        }
    }
    return newList;
}

好啦,以上就是实现多级树形选择列表的方法,直接拷到项目中稍加修改就能用。
如有疑问可以在评论区进行交流。

上一篇下一篇

猜你喜欢

热点阅读