Android开发 ExpandableListView 可折叠列表详解

时间:2021-08-12 00:18:16

前言

  在需要实现一个List的item需要包含列表的时候,我们就可以选择ExpandableListView. 其实这个View的原始设计还是ListView的那套.就是增加2层的ListView而已.所以在写它的适配器与ListView的适配器挺相似的,所以会有一个通病就是没有Item的View的复用机制请一定要注意这点,在实现使用的时候需要写Item的View的复用,减少内存与增加性能.

一个简单的Demo

  老规矩,先来一个最简单的demo来了解下最基本的使用方法.注意!这个demo是没有在Adapter写任何View复用机制的请不要用到实际项目中. demo只是帮助你快速认识了解ExpandableListView

效果图

Android开发 ExpandableListView 可折叠列表详解  

Activity的Xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"> <ExpandableListView
android:id="@+id/expandablelistview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ExpandableListView> </LinearLayout>

一级Item和二级Item用的xml布局

偷懒,我让一级和二级都使用一个布局

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="45dp"
android:text="内容"
android:textSize="15sp"
android:textColor="@color/fontBlack3"
android:gravity="center"
android:background="@color/colorWhite">
</TextView>

编写适配器

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import java.util.List; public class DemoAdapter extends BaseExpandableListAdapter {
List<String> mGroupList;//一级List
List<List<String>> mChildList;//二级List 注意!这里是List里面套了一个List<String>,实际项目你可以写一个pojo类来管理2层数据 public DemoAdapter(List<String> groupList, List<List<String>> childList){
mGroupList = groupList;
mChildList = childList; } @Override
public int getGroupCount() {//返回第一级List长度
return mGroupList.size();
} @Override
public int getChildrenCount(int groupPosition) {//返回指定groupPosition的第二级List长度
return mChildList.get(groupPosition).size();
} @Override
public Object getGroup(int groupPosition) {//返回一级List里的内容
return mGroupList.get(groupPosition);
} @Override
public Object getChild(int groupPosition, int childPosition) {//返回二级List的内容
return mChildList.get(groupPosition).get(childPosition);
} @Override
public long getGroupId(int groupPosition) {//返回一级View的id 保证id唯一
return groupPosition;
} @Override
public long getChildId(int groupPosition, int childPosition) {//返回二级View的id 保证id唯一
return groupPosition + childPosition;
} /**
* 指示在对基础数据进行更改时子ID和组ID是否稳定
* @return
*/
@Override
public boolean hasStableIds() {
return true;
} /**
* 返回一级父View
*/
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_item, parent,false);
((TextView)convertView).setText((String)getGroup(groupPosition));
return convertView;
} /**
* 返回二级子View
*/
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_item, parent,false);
((TextView)convertView).setText((String)getChild(groupPosition,childPosition));
return convertView;
} /**
* 指定位置的子项是否可选
*/
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}

Activity里的代码

        mExpandableListView = findViewById(R.id.expandablelistview);

        List<String> groupList = new ArrayList<>();
groupList.add("一");
groupList.add("二");
groupList.add("三"); List<List<String>> childList = new ArrayList<>();
List<String> childList1 = new ArrayList<>();
childList1.add("1");
childList1.add("1");
childList1.add("1");
List<String> childList2 = new ArrayList<>();
childList2.add("2");
childList2.add("2");
childList2.add("2");
List<String> childList3 = new ArrayList<>();
childList3.add("3");
childList3.add("3");
childList3.add("3"); childList.add(childList1);
childList.add(childList2);
childList.add(childList3); DemoAdapter demoAdapter = new DemoAdapter(groupList, childList);
mExpandableListView.setAdapter(demoAdapter); mExpandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {//一级点击监听
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) { //如果你处理了并且消费了点击返回true,这是一个基本的防止onTouch事件向下或者向上传递的返回机制
return false;
}
}); mExpandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {//二级点击监听
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { //如果你处理了并且消费了点击返回true
return false;
}
});

其他Xml属性

android:dividerHeight="20dp" 设置item间距高度,注意设置这个间距包括了一级和二级

android:divider="@color/colorRed1" 设置一级间距颜色

android:childDivider="@color/colorGreen" 设置二级间距颜色

android:childIndicator:显示在子列表旁边的Drawable对象,可以是一个图像

android:childIndicatorEnd:子列表项指示符的结束约束位置

android:childIndicatorLeft:子列表项指示符的左边约束位置

android:childIndicatorRight:子列表项指示符的右边约束位置

android:childIndicatorStart:子列表项指示符的开始约束位置

android:groupIndicator:显示在组列表旁边的Drawable对象,可以是一个图像

android:indicatorEnd:组列表项指示器的结束约束位置

android:indicatorLeft:组列表项指示器的左边约束位置

android:indicatorRight:组列表项指示器的右边约束位置

android:indicatorStart:组列表项指示器的开始约束位置

可以实现的ExpandableListView3种Adapter

1. 扩展BaseExpandableListAdpter实现ExpandableAdapter。

2. 使用SimpleExpandableListAdpater将两个List集合包装成ExpandableAdapter

3. 使用simpleCursorTreeAdapter

ExpandableListView的一些API详解

mExpandableListView.collapseGroup(position);   收起指定位置组的二级列表

mExpandableListView.expandGroup(position);  展开指定位置组的二级列表

mExpandableListView.isGroupExpanded(position);  指定位置的组是否展开

mExpandableListView.setSelectedGroup(position);  将指定位置的组设置为置顶

改变方向图标的位置

int width = getResources().getDisplayMetrics().widthPixels;
mExpandableListView.setIndicatorBounds(width - UnitConversionUtil.dip2px(this,40)
, width - UnitConversionUtil.dip2px(this,15));//设置图标位置

关于点击事件的一些坑

  如果你在适配器里去实现了Group的点击事件想用回调方法回调出去(如下代码),这个时候你就会碰到一个Group无法展开和收起的坑,原因很简单因为这里已经把点击事件消费了,点击不在继续向下传递,所以底层实现的展开和收起不执行了

  /**
* 返回一级父View
*/
@Override
public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, final ViewGroup parent) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { }
});
return convertView;
}

那么如何解决呢?

方法一

  不用setOnClickListener(),因为这个会消费事件,我们改用setOnTouchListener,如下代码:

      convertView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
          //实现你自己的接口回调
}
return false;
}
});

  这里有一点需要注意ACTION_DOWN 需要返回false,因为底层是消费ACTION_DOWN的来展开和收起的....

方式二

将groupPosition回调到外面后使用collapseGroup() 或者 expandGroup()方法实现.

end