既然大家能到这里来,相信已经对ExpandableListView用了初步的认识,废话不多说,直接给大家上干货。
源程序找不到了,下图是将ExpandableListView放在DrawerLayout(侧边栏)中的效果。

ExpandableListView就是大家平时在PC端见到的二级列表,分为父条目和子条目,通过点击父条目可以展示或隐藏相应的子条目。我们首先创建父条目和子条目的布局,这样可以保证当我们用数据填充ExpandableListView时所有的父条目和所有的子条目能够有整齐划一的样式,保证美观性。
父条目布局group_item.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="match_parent"><TextViewandroid:id="@+id/group_item_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="4dp"android:gravity="center"android:text="父标题"android:textColor="#fff"android:textSize="16sp"android:layoutDirection="ltr"android:background="#24A2D9"/></LinearLayout>
 
我们的父条目只需要展示一个题目,因此布局中只写了一个TextView,如果你的项目中需要在夫条目前边带着图标的话可以再添加ImageView等其他视图。
子条目布局child_item.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="match_parent"><TextViewandroid:id="@+id/child_item_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="14sp"android:text="子项"android:gravity="center"/></LinearLayout>
 
需要的工具已经准备完毕,下面开始使用ExpandableListView
在主活动的布局中添加ExpandableListView控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ThirdActivity"><ExpandableListViewandroid:id="@+id/expandable_listview"android:layout_width="match_parent"android:layout_height="match_parent"></ExpandableListView></LinearLayout> 
在Activity中声明并找到此控件
package com.tjut.ddms;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ExpandableListView;public class ThirdActivity extends AppCompatActivity {private ExpandableListView expandableListView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_third);expandableListView = findViewById(R.id.expandable_listview);}
}
 
下面需要设置适配器,默认使用ExpandableListViewAdapter。 熟悉ListView的小伙伴们肯定都知道使用ArrayAdapter或SimpleAdapter就可以完成数据的适配填充,但是为了提高加载效率和重用性我们通常会使用ViewHolder重写适配器。相信大家也都不希望自己写的代码是低效的,因此这里向大家介绍重写适配器的方法。
- 创建一个java类命名为MyExpandableListViewAdapter并继承BaseExpandableListAdapter
 - 实现必要的方法,一般都是必须要实现的,在下面的代码中详述。其中最重要的两个方法是getGroupView()和getChildView()
 - 实现GroupVIewHolder和ChildViewHolder两个内部类,目的是保存已加载条目再次加载时重用,不明白也无所谓。只需要知道父条目布局中使用了什么控件就在GroupViewHolder中声明什么控件,在子条目布局中使用了什么控件就在CHildViewHolder中声明什么控件
 - 得到数据,完成实现的各个方法,实现数据的填充
 
package com.tjut.adapter;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;import com.tjut.ddms.R;public class MyExpandableListViewAdapter extends BaseExpandableListAdapter {//为了方便我直接把数据写在适配器中了,如果有需要的话可以在适配器的构造方法中获取数据
//需要注意的是子项数据应为二维的,因为每个父条目中都包含一个或几个子条目private String[] groupItems = {"父标题0","父标题1","父标题2","父标题3","父标题4","父标题5"};private String[][] childItems = {{"子标题01","子标题02","子标题03"},{"子标题11","子标题12","子标题13"},{"子标题21","子标题22","子标题23"},{"子标题31","子标题32","子标题33"},{"子标题41","子标题42","子标题43"},{"子标题51","子标题52","子标题53"}};/*** f返回父条目数目* @return*/@Overridepublic int getGroupCount() {return groupItems.length;}/*** 返回子条目数目* @param position 父条目索引* @return*/@Overridepublic int getChildrenCount(int position) {return childItems[position].length;}/*** 返回指定父条目中的数据* @param position 父条目索引* @return*/@Overridepublic Object getGroup(int position) {return groupItems[position];}/*** 返回指定子条目中的数据* @param parentPosition 父条目索引* @param childPosition 子条目索引* @return*/@Overridepublic Object getChild(int parentPosition, int childPosition) {return childItems[parentPosition][childPosition];}/*** 返回指定父条目ID* @param position 父条目索引* @return*/@Overridepublic long getGroupId(int position) {return position;}/*** 返回指定子条目ID* @param parentPosition 父条目索引* @param childPosition 子条目索引* @return*/@Overridepublic long getChildId(int parentPosition, int childPosition) {return childPosition;}/*** 父条目和子条目是否有稳定的ID,如果程序运行过程中不会动态添加或删除父条目或子条目则返回True* @return*/@Overridepublic boolean hasStableIds() {return true;}/*** 加载父条目布局* @param parentPosition 父条目索引* @param isExpandable 指定父条目是否是展开的* @param concertView 重用视图* @param viewGroup 重用视图依附于的视图组* @return 返回重用视图*/@Overridepublic View getGroupView(int parentPosition, boolean isExpandable, View concertView, ViewGroup viewGroup) {GroupViewHolder groupViewHolder = null;if(concertView == null) {//如果重用视图不存在则创造,如果存在则重用,提高加载效率concertView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.group_item,viewGroup,false);groupViewHolder = new GroupViewHolder();groupViewHolder.groupTextView = concertView.findViewById(R.id.group_item_text);concertView.setTag(groupViewHolder);}groupViewHolder = (GroupViewHolder) concertView.getTag();groupViewHolder.groupTextView.setText(groupItems[parentPosition]);return concertView;}/*** 加载对应子条目的布局* @param parentPosition* @param childPosition* @param isExpandable* @param convertView* @param viewGroup* @return*/@Overridepublic View getChildView(int parentPosition, int childPosition, boolean isExpandable, View convertView, ViewGroup viewGroup) {ChildViewHolder childViewHolder = null;if(convertView == null) {//如果重用视图不存在则创造,如果存在则重用,提高加载效率convertView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.child_item,viewGroup,false);childViewHolder = new ChildViewHolder();childViewHolder.childTextView = convertView.findViewById(R.id.child_item_text);convertView.setTag(childViewHolder);}childViewHolder = (ChildViewHolder) convertView.getTag();childViewHolder.childTextView.setText(childItems[parentPosition][childPosition]);return convertView;}/*** 子条目是或否是可选择的* @param i* @param i1* @return*/@Overridepublic boolean isChildSelectable(int i, int i1) {return true;}class GroupViewHolder {TextView groupTextView;}class ChildViewHolder {TextView childTextView;}
}
 
在Activity中声明我们的自定义适配器并调用setAdapter()方法就可以使用了
package com.tjut.ddms;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ExpandableListView;import com.tjut.adapter.MyExpandableListViewAdapter;public class ThirdActivity extends AppCompatActivity {private ExpandableListView expandableListView;private MyExpandableListViewAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_third);expandableListView = findViewById(R.id.expandable_listview);adapter = new MyExpandableListViewAdapter();expandableListView.setAdapter(adapter);}
}
 
- 下面介绍几个常用的点击事件
 
1.OnGroupClickListener()父条目被点击时触发
        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {@Overridepublic boolean onGroupClick(ExpandableListView expandableListView, View view, int parentPosition, long l) {Toast.makeText(ThirdActivity.this,"父条目:" + parentPosition,Toast.LENGTH_SHORT).show();return false;}}); 
2.OnChildClickListener()子条目被点击了触发
       expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView expandableListView, View view, int parentPosition, int childPosition, long l) {Toast.makeText(ThirdActivity.this,"parentPosition = " + parentPosition +",childPosition = " + childPosition,Toast.LENGTH_SHORT).show();return true;}}); 
3.OnGroupExpandListener()父条目展开时触发,利用下面的方法可以实现当一个父条目展开其它父条目均关闭的效果
        expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {@Overridepublic void onGroupExpand(int position) {int count = adapter.getGroupCount();for(int i = 0;i < count;i++) {if(i != position) {expandableListView.collapseGroup(i);}}}}); 
4.OnGroupCollapseListener()当父条目关闭时触发
        expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {@Overridepublic void onGroupCollapse(int i) {}}); 
- 如果需要把左边的指示器移动到右边可以通过两种方法实现
 
1.将父条目的指示器隐藏,在ExpandableListView中添加属性android:groupIndicator="@null",然后在父条目的布局中右侧添加自己准备的指示器图片
2.设置指示器内容从右到左排列,使指示器移动到右边,然后设置父条目布局中的文字从左到右,使文字方向不变
在ExpandableListView中添加属性android:layoutDirection="rtl"
在父条目布局的TextView中添加属性android:layoutDirection="ltr"











