【Android】ExpandableListView的使用

作者: 吾非言 | 来源:发表于2017-09-03 17:29 被阅读457次

    作者:邹峰立,微博:zrunker,邮箱:zrunker@yahoo.com,微信公众号:书客创作,个人平台:www.ibooker.cc

    本文选自书客创作平台第38篇文章。阅读原文

    书客创作

    ExpandableListView可扩展列表,或者称其为二级列表,它能够完美的实现二级列表的显示和隐藏。也是应用中常见的控件之一。ExpandableListView继承ListView,在实现二级列表方面表现优异,不适合多级列表的实现。那么该如何使用ExpandableListView实现一个完整的二级列表呢?
    下面将以一个小案例说明ExpandableListView是如何使用。首先看看效果图:

    效果图

    一、XML布局文件

    ExpandableListView是Android SDK自带的一个控件,该控件继承自ListView,所以ListView的相关属性,它基本上都适合,但不是全部。

    1、添加ExpandableListView到XML布局文件activity_main.xml

    <ExpandableListView
        android:id="@+id/alarm_clock_expandablelist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:childDivider="#c8c7cc"
        android:divider="@null"
        android:dividerHeight="1dp"
        android:gravity="center"
        android:listSelector="#ffffff"
        android:scrollbars="none" />
    

    2、一级列表布局activity_main _father.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical">
    
        <TextView
            android:id="@+id/alarm_clock_father_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            android:padding="10dp"
            android:textSize="18sp" />
    
        <ImageView
            android:id="@+id/group_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginBottom="3dp"
            android:layout_marginEnd="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="3dp"
            android:contentDescription="@null" />
    
    </RelativeLayout>
    

    3、二级列表布局activity_main _children.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">
    
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginStart="10dp"
            android:gravity="center_vertical"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/alarm_clock_tv1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="18sp" />
    
            <TextView
                android:id="@+id/alarm_clock_tv2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="13sp" />
        </LinearLayout>
    
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:contentDescription="@null"
            android:src="@drawable/right" />
    
    </RelativeLayout>
    

    二、创建列表数据对象

    1、一级列表数据对象,这里封装了对应二级列表数据。

    public class FatherData {
        private String title;
        private ArrayList<ChildrenData> list;// 二级列表数据
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public ArrayList<ChildrenData> getList() {
            return list;
        }
        public void setList(ArrayList<ChildrenData> list) {
            this.list = list;
        }
    }
    

    2、二级列表数据对象。

    public class ChildrenData {
        private String Desc;
        private String title;
        public String getDesc() {
            return Desc;
        }
        public void setDesc(String desc) {
            Desc = desc;
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
    }
    

    三、自定义Adapter

    自定义ExPandableListView的适配器需要继承BaseExpandableListAdapter 。并实现一级列表和二级列表的相关处理方法。

    public class ExPandableListViewAdapter extends BaseExpandableListAdapter {
        // 定义一个Context
        private Context context;
        // 定义一个LayoutInflater
        private LayoutInflater mInflater;
        // 定义一个List来保存列表数据
        private ArrayList<FatherData> data_list = new ArrayList<>();
    
        // 定义一个构造方法
        public ExPandableListViewAdapter(Context context, ArrayList<FatherData> datas) {
            this.context = context;
            this.mInflater = LayoutInflater.from(context);
            this.data_list = datas;
        }
    
        // 刷新数据
        public void flashData(ArrayList<FatherData> datas) {
            this.data_list = datas;
            this.notifyDataSetChanged();
        }
    
        // 获取二级列表的内容
        @Override
        public Object getChild(int arg0, int arg1) {
            return data_list.get(arg0).getList().get(arg1);
        }
    
        // 获取二级列表的ID
        @Override
        public long getChildId(int arg0, int arg1) {
            return arg1;
        }
    
        // 定义二级列表中的数据
        @Override
        public View getChildView(int arg0, int arg1, boolean arg2, View arg3, ViewGroup arg4) {
            // 定义一个二级列表的视图类
            HolderView childrenView;
            if (arg3 == null) {
                childrenView = new HolderView();
                // 获取子视图的布局文件
                arg3 = mInflater.inflate(R.layout.activity_main_children, arg4, false);
                childrenView.titleView = (TextView) arg3.findViewById(R.id.alarm_clock_tv1);
                childrenView.descView = (TextView) arg3.findViewById(R.id.alarm_clock_tv2);
                // 这个函数是用来将holderview设置标签,相当于缓存在view当中
                arg3.setTag(childrenView);
            } else {
                childrenView = (HolderView) arg3.getTag();
            }
    
            /**
             * 设置相应控件的内容
             */
            // 设置标题上的文本信息
            childrenView.titleView.setText(data_list.get(arg0).getList().get(arg1).getTitle());
            // 设置副标题上的文本信息
            childrenView.descView.setText(data_list.get(arg0).getList().get(arg1).getDesc());
            return arg3;
        }
    
        // 保存二级列表的视图类
        private class HolderView {
            TextView titleView;
            TextView descView;
        }
    
        // 获取二级列表的数量
        @Override
        public int getChildrenCount(int arg0) {
            return data_list.get(arg0).getList().size();
        }
    
        // 获取一级列表的数据
        @Override
        public Object getGroup(int arg0) {
            return data_list.get(arg0);
        }
    
        // 获取一级列表的个数
        @Override
        public int getGroupCount() {
            return data_list.size();
        }
    
        // 获取一级列表的ID
        @Override
        public long getGroupId(int arg0) {
            return arg0;
        }
    
        // 设置一级列表的view
        @Override
        public View getGroupView(int arg0, boolean arg1, View arg2, ViewGroup arg3) {
            HodlerViewFather hodlerViewFather;
            if (arg2 == null) {
                hodlerViewFather = new HodlerViewFather();
                arg2 = mInflater.inflate(R.layout.activity_main_father, arg3, false);
                hodlerViewFather.titlev = (TextView) arg2.findViewById(R.id.alarm_clock_father_tv);
                // 新建一个TextView对象,用来显示一级标签上的大体描述的信息
                hodlerViewFather.group_state = (ImageView) arg2.findViewById(R.id.group_state);
                arg2.setTag(hodlerViewFather);
            } else {
                hodlerViewFather = (HodlerViewFather) arg2.getTag();
            }
            // 一级列表右侧判断箭头显示方向
            if (arg1) {
                hodlerViewFather.group_state.setImageResource(R.drawable.group_down);
            } else {
                hodlerViewFather.group_state.setImageResource(R.drawable.group_up);
            }
            /**
             * 设置相应控件的内容
             */
            // 设置标题上的文本信息
            hodlerViewFather.titlev.setText(data_list.get(arg0).getTitle());
    
            // 返回一个布局对象
            return arg2;
        }
    
        // 定义一个 一级列表的view类
        private class HodlerViewFather {
            TextView titlev;
            ImageView group_state;
        }
    
        /**
         * 指定位置相应的组视图
         */
        @Override
        public boolean hasStableIds() {
            return true;
        }
    
        /**
         * 当选择子节点的时候,调用该方法(点击二级列表)
         */
        @Override
        public boolean isChildSelectable(int arg0, int arg1) {
            return true;
        }
    }
    

    四、Activity实现

    ExPandableListView提供对一级列表和二级列表的点击事件监听,分别是OnGroupClickListener、OnChildClickListener。

    public class MainActivity extends AppCompatActivity {
        private ExpandableListView myExpandableListView;
        private ExPandableListViewAdapter adapter;
        private ArrayList<FatherData> datas;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initView();
            setData();
            setAdapter();
        }
    
        // 初始化控件
        private void initView() {
            myExpandableListView = (ExpandableListView) findViewById(R.id.alarm_clock_expandablelist);
            // 设置ExpandableListView的监听事件
            // 设置一级item点击的监听器
            myExpandableListView.setOnGroupClickListener(new OnGroupClickListener() {
                @Override
                public boolean onGroupClick(ExpandableListView arg0, View arg1, int arg2, long arg3) {
                    // TODO Auto-generated method stub
                    Toast.makeText(MainActivity.this, datas.get(arg2).getTitle(), Toast.LENGTH_LONG).show();
                    return false;
                }
            });
    
            // 设置二级item点击的监听器,同时在Adapter中设置isChildSelectable返回值true,同时二级列表布局中控件不能设置点击效果
            myExpandableListView.setOnChildClickListener(new OnChildClickListener() {
                @Override
                public boolean onChildClick(ExpandableListView arg0, View arg1, int arg2, int arg3, long arg4) {
                    // TODO Auto-generated method stub
                    Toast.makeText(MainActivity.this, datas.get(arg2).getList().get(arg3).getTitle(), Toast.LENGTH_LONG).show();
                    return false;
                }
            });
        }
    
        /**
         * 自定义setAdapter
         */
        private void setAdapter() {
            if (adapter == null) {
                adapter = new ExPandableListViewAdapter(this, datas);
                myExpandableListView.setAdapter(adapter);
            } else {
                adapter.flashData(datas);
            }
        }
    
        // 定义数据
        private void setData() {
            if (datas == null) {
                datas = new ArrayList<>();
            }
            // 一级列表中的数据
            for (int i = 0; i < 5; i++) {
                FatherData fatherData = new FatherData();
                fatherData.setTitle("闹钟列表" + i);
                // 二级列表中的数据
                ArrayList<ChildrenData> itemList = new ArrayList<>();
                for (int j = 0; j < 3; j++) {
                    ChildrenData childrenData = new ChildrenData();
                    childrenData.setTitle("闹钟主题" + j);
                    childrenData.setDesc(j + ":30");
                    itemList.add(childrenData);
                }
                fatherData.setList(itemList);
                datas.add(fatherData);
            }
        }
    }
    

    以上便是实现可扩展列表的基本使用规则。

    ExPandableListView其他设置

    (1)、去掉默认带的箭头

    expandlistView.setGroupIndicator(null);
    

    (2)、设置默认选中项

    expandlistView.setSelection(0);
    

    (3)、所有项设置成默认展开

    // 遍历所有group,将所有项设置成默认展开
    int groupCount = expandlistView.getCount();
    for (int i = 0; i < groupCount; i++) {
        expandlistView.expandGroup(i);
    }
    

    ExPandableListView常见问题

    在开发过程常常会遇到ExPandableListView二级列表点击没有反应,那么该如何正确实现二级列表的点击事件呢?

    一、需要在Adapter复写isChildSelectable方法,设置返回值为true。

    @Override
    public boolean isChildSelectable(int arg0, int arg1) {
            return true;
    }
    

    二、如果这样二级列表点击还没有效果的话,检查看看二级布局文件是不是设置了布局点击事件,如果二级列表布局已经设置了点击事件就去掉布局的点击事件。

    三、如果以上两种方式都处理了,还是无法设置二级列表的点击事件,那就检查二级列表布局文件中是否含有像ToggleButton这样原本就具有点击效果的控件,如果有就去掉。

    Github地址
    阅读原文


    微信公众号:书客创作

    相关文章

      网友评论

        本文标题:【Android】ExpandableListView的使用

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