美文网首页Android充电站安卓Android开发
Android多级树形选择列表案例 - 手把手教你快速实现

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

作者: 86cf4d336efc | 来源:发表于2017-05-22 16:31 被阅读7849次

    公司项目中有一个选择联系人的界面,一看里面关系极其复杂,最多时有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;
    }
    

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/fyrpxxtx.html