美文网首页
Android 二级列表控件ExpandableListView

Android 二级列表控件ExpandableListView

作者: 隐姓埋名的猫大叔 | 来源:发表于2020-11-17 22:16 被阅读0次

    简述

    在Android 开发中多多少少会碰到需要二级列表,之前已经写过一篇RecyclerView 二级列表 其实现方式是通过根据不同ViewHolder 来显示是一级还是二级列表,想起谷歌官方自己就有自带二级列表控件ExpandableListView,如果不需要复杂效果,建议直接使用官方控件,故有了今天这一篇文章。

    老规矩,先上图:


    视频录制效果.gif

    如图所见滑动出屏幕或者点击checkbox时会出现错位等一些问题也解决了,具体方法请往下浏览(文末附上github 地址)

    页面布局

    布局很简单,ExpandableListView 加底部一个Button,直接上布局截图,相信各位能看懂


    activity_main.png

    由于其控件会默认自带箭头(如下图)


    默认自带的指示器箭头.png

    我们可以通过XML中在ExpandableListView控件加上

         android:groupIndicator="@null"
    

    取消掉其自带的指示器箭头,当然除了在xml上,也可通过在代码中,当绑定完控件后调用代码也可实现取消效果

     expandableListView.setGroupIndicator(null);
    

    接下来是我们重点要研究的适配器StudentExpandableAdapter,继承并重写了BaseExpandableListAdapter这个类的相关函数,其中注释我已经详细写在代码中,若是不懂或者写错,希望各位可以交流或指出,大家一起加深对其认识。

    public class StudentExpandableAdapter extends BaseExpandableListAdapter {
    
        private Context context;
        private List<DataEntity> dataEntity;
        private CheckBoxListener checkBoxListener;
    
        public StudentExpandableAdapter(Context context, List<DataEntity> dataEntity) {
            this.context = context;
            this.dataEntity = dataEntity;
        }
    
        /**
         * 获取组的数目
         *
         * @return 返回一级列表组的数量
         */
        @Override
        public int getGroupCount() {
            return dataEntity == null ? 0 : dataEntity.size();
        }
    
        /**
         * 获取指定组中的子节点数量
         *
         * @param groupPosition 子元素组所在的位置
         * @return 返回指定组中的子数量
         */
        @Override
        public int getChildrenCount(int groupPosition) {
            return dataEntity.get(groupPosition).getChildrenDataList().size();
        }
    
        /**
         * 获取与给定组相关联的对象
         *
         * @param groupPosition 子元素组所在的位置
         * @return 返回指定组的子数据
         */
        @Override
        public Object getGroup(int groupPosition) {
            return dataEntity.get(groupPosition).getTitle();
        }
    
    
        /**
         * 获取与给定组中的给定子元素关联的数据
         *
         * @param groupPosition 子元素组所在的位置
         * @param childPosition 子元素的位置
         * @return 返回子元素的对象
         */
        @Override
        public Object getChild(int groupPosition, int childPosition) {
            return dataEntity.get(groupPosition).getChildrenDataList().get(childPosition);
        }
    
        /**
         * 获取组在给定位置的ID(唯一的)
         *
         * @param groupPosition 子元素组所在的位置
         * @return 返回关联组ID
         */
        @Override
        public long getGroupId(int groupPosition) {
            return groupPosition;
        }
    
    
        /**
         * 获取给定组中给定子元素的ID(唯一的)
         *
         * @param groupPosition 子元素组所在的位置
         * @param childPosition 子元素的位置
         * @return 返回子元素关联的ID
         */
        @Override
        public long getChildId(int groupPosition, int childPosition) {
            return childPosition;
        }
    
        /**
         * @return 确定id 是否总是指向同一个对象
         */
        @Override
        public boolean hasStableIds() {
            return true;
        }
    
        /**
         * @return 返回指定组的对应的视图 (一级列表样式)
         */
        @Override
        public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
            ParentHolder parentHolder;
            if (convertView == null) {
                convertView = LayoutInflater.from(context).inflate(R.layout.parent_item, null);
                parentHolder = new ParentHolder();
                parentHolder.tvParent = convertView.findViewById(R.id.tv_parent);
                parentHolder.img_right = convertView.findViewById(R.id.img_right);
                convertView.setTag(parentHolder);
            } else {
                parentHolder = (ParentHolder) convertView.getTag();
            }
            parentHolder.tvParent.setText(dataEntity.get(groupPosition).getTitle());
         
    
            //共用一个右箭头,如果展开则顺时针旋转90°选择,否则不旋转
            if (isExpanded) parentHolder.img_right.setRotation(90F);
            else parentHolder.img_right.setRotation(0F);
    
            return convertView;
        }
    
        /**
         * @return 返回指定位置对应子视图的视图
         */
        @Override
        public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
            final ChildrenHolder childrenHolder;
            if (convertView == null) {
                convertView = LayoutInflater.from(context).inflate(R.layout.childrens_item, null);
                childrenHolder = new ChildrenHolder();
                childrenHolder.tvChild = convertView.findViewById(R.id.tv_child);
                childrenHolder.checkBox = convertView.findViewById(R.id.checkbox);
                convertView.setTag(childrenHolder);
            } else {
                childrenHolder = (ChildrenHolder) convertView.getTag();
            }
    
    
            //Log.e("666","班级:"+dataEntity.get(groupPosition).getTitle()+"    学生:"+dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).getSubContent()+"   isChecked:"+dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).isSelect());
            childrenHolder.checkBox.setChecked(dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).isSelect());
            childrenHolder.tvChild.setText(dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).getSubContent());
            childrenHolder.checkBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    boolean isChecked = !dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).isSelect();
                    dataEntity.get(groupPosition).getChildrenDataList().get(childPosition).setSelect(!isChecked);
                    Log.e("groupPosition:" + groupPosition, "childPosition:" + childPosition + " isChecked:" + isChecked);
                    checkBoxListener.checkStateListener(groupPosition, childPosition, isChecked);
                }
            });
    
    
            return convertView;
        }
    
        /**
         * 指定位置的子元素是否可选
         *
         * @param groupPosition 子元素组所在的位置
         * @param childPosition 子元素的位置
         * @return 返回是否可选
         */
    
        @Override
        public boolean isChildSelectable(int groupPosition, int childPosition) {
            return true;
        }
    
    
        class ParentHolder {
            TextView tvParent;
            ImageView img_right;
        }
    
    
        class ChildrenHolder {
            TextView tvChild;
            CheckBox checkBox;
        }
    
    
        /**
         * 用于提供对外复选框修改通知接口
         */
        public interface CheckBoxListener {
            void checkStateListener(int groupPosition, int childPosition, boolean isChecked);
        }
    
        public void setCheckBoxListener(CheckBoxListener checkBoxListener) {
            this.checkBoxListener = checkBoxListener;
        }
    
    
        /**
         * 用于刷新更新后的数据
         */
        public void reFreshData(List<DataEntity> dataEntity) {
            this.dataEntity = dataEntity;
            notifyDataSetChanged();
        }
    
    
    }
    

    父布局使用的xml:


    parent_item.png

    子布局使用的xml:


    childrens_item.png

    注意在getGroupView中getGroupView的控件不能设置一些抢占焦点的事件或属性,如点击事件或者在代码布局里设置了focusable属性为true,都会导致无法展开子列表。

    getChildView中子视图,checkBox不调用setOnCheckedChangeListener是由于可能会因为选中的组展开触发而导致混乱,这边改为使用setOnClickListener,这是一种折中方案,因为状态的改变不是来自事件onClick(也就是你点击了不一定知道状态是否成功更改),OnCheckChangedListener则是监听CheckBox的状态,成功后回调。

    实体类DataEntity 代码如下

    public class DataEntity {
        private String title;//一级列表内容
        private List<ChildrenData> childrenDataList;
    
    
        public DataEntity(String title, List<ChildrenData> childrenDataList) {
            this.title = title;
            this.childrenDataList = childrenDataList;
        }
    
        public List<ChildrenData> getChildrenDataList() {
            return childrenDataList;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public void setChildrenDataList(List<ChildrenData> childrenDataList) {
            this.childrenDataList = childrenDataList;
        }
    
        public static class ChildrenData{
            private String subContent;//子内容
            private boolean select;//是否选中
    
            public ChildrenData(String subContent, boolean select) {
                this.subContent = subContent;
                this.select = select;
            }
    
            public String getSubContent() {
                return subContent;
            }
    
            public void setSubContent(String subContent) {
                this.subContent = subContent;
            }
    
            public boolean isSelect() {
                return select;
            }
    
            public void setSelect(boolean select) {
                this.select = select;
            }
        }
    }
    

    最后是在我们的主界面中实现代码

    public class MainActivity extends AppCompatActivity {
        private ExpandableListView expandableListView;
        private Button btn_select;
        private boolean selectAll;
        private List<DataEntity>  dataEntityList=new ArrayList<>();
        private StudentExpandableAdapter  studentExpandableAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            expandableListView =findViewById(R.id.listView);
            btn_select=findViewById(R.id.btn_select);
            initData();
            initAdapter();
            setOnClickEvent();
        }
    
        private void initData() {
    
            for(int i=0;i<5;i++){
                List<DataEntity.ChildrenData> childrenData=new ArrayList<>();
                for(int j=0;j<8;j++){
                    DataEntity.ChildrenData children=new DataEntity.ChildrenData("学生"+(j+1),false);
                    childrenData.add(children);
                }
                DataEntity dataEntity=new DataEntity((i+1)+"班",childrenData);
                dataEntityList.add(dataEntity);
            }
        }
    
        private void setOnClickEvent() {
            btn_select.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectAll=!selectAll;
                    if(selectAll){
                        //遍历设置全选
                        for(int i=0;i<dataEntityList.size();i++){
                            for(int j=0;j<dataEntityList.get(i).getChildrenDataList().size();j++){
                                dataEntityList.get(i).getChildrenDataList().get(j).setSelect(true);
                            }
                        }
                    }else {
                        //遍历设置取消全选
                        for(int i=0;i<dataEntityList.size();i++){
                            for(int j=0;j<dataEntityList.get(i).getChildrenDataList().size();j++){
                                dataEntityList.get(i).getChildrenDataList().get(j).setSelect(false);
                            }
                        }
                    }
    
    
                    studentExpandableAdapter.reFreshData(dataEntityList);
    
                    btn_select.setText(selectAll? "取消全选":"全选");
    
                }
            });
        }
    
        private void initAdapter() {
            studentExpandableAdapter=new StudentExpandableAdapter(this,dataEntityList);
            expandableListView.setAdapter(studentExpandableAdapter);
    
            studentExpandableAdapter.setCheckBoxListener(new StudentExpandableAdapter.CheckBoxListener() {
                @Override
                public void checkStateListener(int groupPosition, int childPosition, boolean isChecked) {
                    Log.e("MainActivity","isChecked:"+isChecked);
                    dataEntityList.get(groupPosition).getChildrenDataList().get(childPosition).setSelect(isChecked);
                    studentExpandableAdapter.reFreshData(dataEntityList);
                }
            });
    
    
            /**
             * 默认展开某个item
             * */
            //expandableListView.expandGroup(1);
    
    
        }
    }
    

    至此,简单讲完了ExpandableListView 的基础使用,希望能对小伙伴们提供一点帮助。
    最后附上该项目的github

    相关文章

      网友评论

          本文标题:Android 二级列表控件ExpandableListView

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