一. 前言
1. 什么是ExpandableListView?
| 12
 3
 4
 5
 6
 7
 
 | java.lang.Object↳android.view.View
 ↳android.view.ViewGroup
 ↳android.widget.AdapterView<T extends android.widget.Adapter>
 ↳android.widget.AbsListView
 ↳android.widget.ListView
 ↳android.widget.ExpandableListView
 
 | 
根据继承图来看,ExpandableListView是ListView的子类。而expandable 在英文中的意思是可扩展的,所以ExpandableListView就是一个可以扩展的、有层级的ListView。
二. 属性 & 属性
1. 指示器
| 12
 3
 
 | android:groupIndicator 
 android:childIndicator
 
 | 
① 如果想取消默认的 gropIndicator,可以将它赋值为”@null”,即:android:groupIndicator="@null"。
② 替换默认indicator时,我们使用了一个selector,在编写这个selector时需要特别注意,我们用的状态是 state_expanded,这个状态在编写的时候不会自动补全,必须完全手动拼写!!
③ 默认情况下,指示器会覆盖组条目内容。为了避免这种情况,我们就需要手动的在布局文件中或者代码中将组条目向右移,以保证条目内容不被覆盖。
④ 例子
| 12
 3
 4
 5
 
 | <?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:drawable="@drawable/weixin_friend" android:state_expanded="true"/>
 <item android:drawable="@drawable/xiangguan"/>
 </selector>
 
 | 
2. 普通方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | public boolean collapseGroup(int groupPos)收起 groupPos 位置的分组
 
 public boolean expandGroup(int groupPos)
 展开 groupPos 位置的分组
 
 public boolean isGroupExpanded(int groupPosition)
 判断 groupPosition 位置的分组是否展开
 
 public void setAdapter(ExpandableListAdapter adapter)
 给 ExpandableListView 设置适配器
 
 | 
3. 监听方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | public void setOnChildClickListener(OnChildClickListener onChildClickListener)设置分组中子条目的点击监听器
 
 public void setOnGroupClickListener(OnGroupClickListener listener)
 设置分组的点击监听器
 
 public void setOnGroupCollapseListener(OnGroupCollapseListener listener)
 设置分组收起的监听器
 
 public void setOnGroupExpandListener(OnGroupExpandListener listener)
 设置分组展开的监听器
 
 | 
4. 其他的属性
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | divider        dividerHeight
 
 childDivider
 
 indicatorLeft
 indicatorStart
 
 indicatorRight
 indicatorEnd
 
 | 
三. BaseExpandableListAdapter
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 
 | class MyAdapter extends BaseExpandableListAdapter {
 
 @Override
 public int getGroupCount() {
 
 }
 
 @Override
 public int getChildrenCount(int groupPosition) {
 
 }
 
 @Override
 public Object getGroup(int groupPosition) {
 
 }
 
 @Override
 public Object getChild(int groupPosition, int childPosition) {
 
 }
 
 @Override
 public long getGroupId(int groupPosition) {
 
 }
 
 @Override
 public long getChildId(int groupPosition, int childPosition) {
 
 }
 
 @Override
 public boolean hasStableIds() {
 
 }
 
 @Override
 public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
 
 }
 
 @Override
 public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
 
 }
 
 @Override
 public boolean isChildSelectable(int groupPosition, int childPosition) {
 
 }
 }
 
 | 
关于hasStableIds的作用
参考:hasStableIds的作用
四. 实际例子
1. 自定义的ExpandableListView包括内部类的适配器
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 
 | public class XLExpandableListView extends ExpandableListView {
 
 
 
 
 public XLExpandableListView(Context context) {
 super(context);
 
 
 init();
 }
 
 
 
 
 
 
 public XLExpandableListView(Context context, AttributeSet attrs) {
 super(context, attrs);
 
 
 init();
 }
 
 
 
 
 private void init() {
 MyAdapter adapter = new MyAdapter();
 setAdapter(adapter);
 }
 
 
 
 
 class MyAdapter extends BaseExpandableListAdapter {
 
 
 @Override
 public int getGroupCount() {
 return DataUtil.getGroups().size();
 }
 
 
 @Override
 public int getChildrenCount(int groupPosition) {
 return DataUtil.getItems().get(groupPosition).size();
 }
 
 
 @Override
 public Object getGroup(int groupPosition) {
 return DataUtil.getGroups().get(groupPosition);
 }
 
 
 @Override
 public Object getChild(int groupPosition, int childPosition) {
 return DataUtil.getItems().get(groupPosition).get(childPosition);
 }
 
 
 @Override
 public long getGroupId(int groupPosition) {
 return groupPosition;
 }
 
 
 @Override
 public long getChildId(int groupPosition, int childPosition) {
 return childPosition;
 }
 
 
 @Override
 public boolean hasStableIds() {
 return false;
 }
 
 
 @SuppressLint("InflateParams")
 @Override
 public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
 if (convertView == null){
 convertView = LayoutInflater.from(getContext()).inflate(R.layout.group_layout,null);
 }
 
 TextView group_name_view = convertView.findViewById(R.id.group_name);
 group_name_view.setText((String) getGroup(groupPosition));
 
 return convertView;
 }
 
 
 @SuppressLint("InflateParams")
 @Override
 public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
 if (convertView == null){
 convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_layout,null);
 }
 
 TextView item_name_view = convertView.findViewById(R.id.item_name);
 item_name_view.setText((String) getChild(groupPosition,childPosition));
 
 return convertView;
 }
 
 
 @Override
 public boolean isChildSelectable(int groupPosition, int childPosition) {
 return true;
 }
 }
 }
 
 | 
2. group和item的布局
① group的布局
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | <?xml version="1.0" encoding="utf-8"?><RelativeLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:paddingTop="10dp"
 android:paddingBottom="10dp"
 android:background="#22666666">
 
 <TextView
 android:id="@+id/group_name"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="组名"
 android:textSize="18sp"
 android:layout_marginStart="50dp"
 android:layout_centerVertical="true"
 />
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="4/18"
 android:textSize="16sp"
 android:layout_alignParentEnd="true"
 android:layout_marginEnd="10dp"
 android:layout_centerVertical="true"
 />
 
 
 </RelativeLayout>
 
 | 
② item的布局
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | <?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:orientation="horizontal"
 android:gravity="center_vertical"
 android:paddingTop="10dp"
 android:paddingBottom="10dp"
 >
 
 <ImageView
 android:layout_width="40dp"
 android:layout_height="40dp"
 android:src="@mipmap/ic_launcher_round"
 android:scaleType="fitXY"
 android:layout_marginStart="40dp"
 />
 
 <LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:orientation="vertical"
 android:layout_marginStart="12dp"
 >
 
 <TextView
 android:id="@+id/item_name"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="好友名"
 android:textStyle="bold"
 android:textSize="16sp"
 />
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="凡事都有偶然的凑巧"
 android:textSize="14sp"
 />
 
 </LinearLayout>
 
 
 </LinearLayout>
 
 | 
3. 数据源
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 
 | public class DataUtil {
 
 
 
 public static List<String> getGroups(){
 
 List<String> groups = new ArrayList<>();
 
 
 groups.add("我的家人");
 groups.add("我的朋友");
 groups.add("黑名单");
 
 return groups;
 }
 
 
 
 
 
 public static List<List<String>> getItems(){
 
 List<List<String>> items = new ArrayList<>();
 
 
 List<String> group_1_item = new ArrayList<>();
 group_1_item.add("爸爸");
 group_1_item.add("妈妈");
 group_1_item.add("哥哥");
 items.add(group_1_item);
 List<String> group_2_item = new ArrayList<>();
 group_2_item.add("张三");
 group_2_item.add("李四");
 group_2_item.add("王二麻");
 items.add(group_2_item);
 List<String> group_3_item = new ArrayList<>();
 group_3_item.add("卖茶叶");
 group_3_item.add("微商");
 group_3_item.add("盗号");
 items.add(group_3_item);
 
 return items;
 }
 }
 
 | 
4. 使用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | <?xml version="1.0" encoding="utf-8"?><RelativeLayout 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=".MainActivity">
 
 <swu.xl.expandablelistview.XLExpandableListView
 android:id="@+id/expand_list_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:groupIndicator="@drawable/indicator"
 />
 
 </RelativeLayout>
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | public class MainActivity extends AppCompatActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 XLExpandableListView listView = findViewById(R.id.expand_list_view);
 
 
 listView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
 @Override
 public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
 Toast.makeText(MainActivity.this, DataUtil.getGroups().get(groupPosition)+"被点击了", Toast.LENGTH_SHORT).show();
 
 return false;
 }
 });
 
 
 listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
 @Override
 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
 Toast.makeText(MainActivity.this, DataUtil.getItems().get(groupPosition).get(childPosition)+"被点击了", Toast.LENGTH_SHORT).show();
 return true;
 }
 });
 
 }
 }
 
 | 
5. Github地址
ExpandableListView
6. 运行效果

五. 问题解决
1. 去除默认点击水波纹效果
| 12
 
 | <item name="android:colorControlHighlight">@android:color/transparent</item>
 
 | 
六. 参考文章
ExpandableListView
安卓的ExpandableListView的使用和优化
ExpandableListView–基本使用介绍