前言
在需要實現一個List的item需要包含列表的時候,我們就可以選擇ExpandableListView. 其實這個View的原始設計還是ListView的那套.就是增加2層的ListView而已.所以在寫它的適配器與ListView的適配器挺相似的,所以會有一個通病就是沒有Item的View的復用機制請一定要注意這點,在實現使用的時候需要寫Item的View的復用,減少內存與增加性能.
一個簡單的Demo
老規矩,先來一個最簡單的demo來了解下最基本的使用方法.注意!這個demo是沒有在Adapter寫任何View復用機制的請不要用到實際項目中. demo只是幫助你快速認識了解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