Android 二级列表控件ExpandableListView

2020-11-17  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读