相關文章:
Android中ExpandableListView的使用(二)
ExpandableListView是可擴展的下拉列表,它的可擴展性在於點擊父item可以拉下或收起列表,適用於一些場景的使用,下面介紹的是在Activity中如何使用,關於它的各種樣式的詳細解釋請見另一篇文章:Android中ExpandableListView常用屬性總結
下面介紹它的基本使用方法
先看一下效果:
一、最基本的使用
新建一個佈局文件expandable_layout.xml,內容很簡單,一個LinearLayout裏面包含了一個ExpandableListView,別忘了給它加上id:
-
<?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"
-
android:orientation="vertical">
-
<ExpandableListView
-
android:id="@+id/expandablelistview"
-
android:layout_margin="5dp"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content" />
-
</LinearLayout>
然後在activity文件中引入這個layout,獲取這個ExpandableListView:
-
private ExpandableListView listview;
-
setContentView(R.layout.expandable_layout);
-
listview = (ExpandableListView) findViewById(R.id.expandablelistview);
爲了給ExpandableListView提供數據,需要先初始化數據,這裏使用一個Map來存放數據,類型爲<String, List<String>>:
-
private Map<String, List<String>> dataset = new HashMap<>();
-
private String[] parentList = new String[]{"first", "second", "third"};
-
private List<String> childrenList1 = new ArrayList<>();
-
private List<String> childrenList2 = new ArrayList<>();
-
private List<String> childrenList3 = new ArrayList<>();
-
private void initialData() {
-
childrenList1.add(parentList[0] + "-" + "first");
-
childrenList1.add(parentList[0] + "-" + "second");
-
childrenList1.add(parentList[0] + "-" + "third");
-
childrenList2.add(parentList[1] + "-" + "first");
-
childrenList2.add(parentList[1] + "-" + "second");
-
childrenList2.add(parentList[1] + "-" + "third");
-
childrenList3.add(parentList[2] + "-" + "first");
-
childrenList3.add(parentList[2] + "-" + "second");
-
childrenList3.add(parentList[2] + "-" + "third");
-
dataset.put(parentList[0], childrenList1);
-
dataset.put(parentList[1], childrenList2);
-
dataset.put(parentList[2], childrenList3);
-
}
然後需要自己實現一個Adapte類,用於爲ExpandableListView提供數據,該類繼承了BaseExpandableListAdapter,下面這個是最簡單的自定義的類:
-
private class MyExpandableListViewAdapter extends BaseExpandableListAdapter {
-
-
-
@Override
-
public Object getChild(int parentPos, int childPos) {
-
return dataset.get(parentList[parentPos]).get(childPos);
-
}
-
-
-
@Override
-
public int getGroupCount() {
-
return dataset.size();
-
}
-
-
-
@Override
-
public int getChildrenCount(int parentPos) {
-
return dataset.get(parentList[parentPos]).size();
-
}
-
-
-
@Override
-
public Object getGroup(int parentPos) {
-
return dataset.get(parentList[parentPos]);
-
}
-
-
-
@Override
-
public long getGroupId(int parentPos) {
-
return parentPos;
-
}
-
-
-
@Override
-
public long getChildId(int parentPos, int childPos) {
-
return childPos;
-
}
-
-
-
@Override
-
public boolean hasStableIds() {
-
return false;
-
}
-
-
-
@Override
-
public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {
-
return view;
-
}
-
-
-
@Override
-
public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {
-
return view;
-
}
-
-
-
@Override
-
public boolean isChildSelectable(int i, int i1) {
-
return false;
-
}
-
}
其中註釋說明了每個方法的作用,這個adapter的所有數據來源都是剛剛初始化的dataset,因此當這個adapter需要返回父項的數目時,返回的就是dataset的大小,如果需要返回某個父項的某個子項時,通過父項的position,使用map的get方法即可獲得。自定義的類中最重要的是下面這兩個方法,下面先介紹getGroupView方法:
-
-
@Override
-
public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {
-
if (view == null) {
-
LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity
-
.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
view = inflater.inflate(R.layout.parent_item, null);
-
}
-
view.setTag(R.layout.parent_item, parentPos);
-
view.setTag(R.layout.child_item, -1);
-
TextView text = (TextView) view.findViewById(R.id.parent_title);
-
text.setText(parentList[parentPos]);
-
return view;
-
}
這個方法用來指定父項顯示的樣式,內容和行爲,其中使用了parent_item佈局:
-
<?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">
-
<TextView
-
android:id="@+id/parent_title"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:textSize="20sp"
-
android:textColor="@color/black"
-
android:textStyle="bold"
-
android:text="這是父item"
-
android:layout_margin="5dp"/>
-
</LinearLayout>
佈局很簡單,只是一個LinearLayout包含了一個TextView。
getGroupView方法先判斷view是否爲空,如果view不爲空,說明已經加載過一次parent_item佈局,因此不需要重複加載以提高效率。如果view爲空,那麼使用如下方法加載parent_item佈局:
-
if (view == null) {
-
LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity
-
.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
view = inflater.inflate(R.layout.parent_item, null);
-
}
然後定義父項的內容和行爲,這裏只需要定義父項的內容,先通過id獲得parent_item佈局中的TextView,然後通過方法提供的parentPos參數獲取到存放在dataset中的內容,調用TextView的setText方法即可設置父項要顯示的內容:
- TextView text = (TextView) view.findViewById(R.id.parent_title);
- text.setText(parentList[parentPos]);
然後將view返回即可。
接下來是getChildView方法:
-
-
@Override
-
public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {
-
if (view == null) {
-
LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity
-
.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
view = inflater.inflate(R.layout.child_item, null);
-
}
-
view.setTag(R.layout.parent_item, parentPos);
-
view.setTag(R.layout.child_item, childPos);
-
TextView text = (TextView) view.findViewById(R.id.child_title);
-
text.setText(dataset.get(parentList[parentPos]).get(childPos));
-
text.setOnClickListener(new View.OnClickListener() {
-
@Override
-
public void onClick(View view) {
-
Toast.makeText(ExpandableListViewTestActivity.this, "點到了內置的textview", Toast.LENGTH_SHORT).show();
-
}
-
});
-
return view;
-
}
這個方法用於定義子項的佈局,內容和行爲,同樣需要一個child_item來指定子項的佈局:
-
<?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">
-
<TextView
-
android:id="@+id/child_title"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:textSize="18sp"
-
android:textColor="@color/black"
-
android:text="這是子item"
-
android:layout_margin="5dp"/>
-
</LinearLayout>
關於view的處理和父項一樣,這裏在方法中還給子項顯示內容的textview添加了一個點擊的監聽器。
adapter自定義完之後,通過聲明一個Adapter,實例化後調用setAdapter方法即可:
-
private MyExpandableListViewAdapter adapter;
-
adapter = new MyExpandableListViewAdapter();
-
listview.setAdapter(adapter);
至此,最基本的ExpandableListView的使用就可以滿足啦
二、稍微複雜一點的使用方法
1、爲子項添加點擊的監聽事件,效果圖:
只需要調用ExpandableListView的setOnChildClickListener方法即可:
-
listview.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
-
@Override
-
public boolean onChildClick(ExpandableListView expandableListView, View view,
-
int parentPos, int childPos, long l) {
-
Toast.makeText(ExpandableListViewTestActivity.this,
-
dataset.get(parentList[parentPos]).get(childPos), Toast.LENGTH_SHORT).show();
-
return true;
-
}
-
});
這裏在每個子項被點擊了之後會顯示是哪個子項被點擊了
特別注意
(1)在使用這個方法的時候需要將自定義的adapter中的isChildSelectable方法的返回值設置爲true,否則子項的點擊不生效,但子項佈局中設置的控件的監聽器依然可以生效。
-
- @Override
- public boolean isChildSelectable(int i, int i1) {
- return true;
- }
(2)如果在子項中對某個控件設置了監聽器,這個控件要注意不能鋪滿整個子項,所以在設置高度和寬度時要特別注意,否則設置了子項的監聽器也是沒有用的
2、爲子項添加長按的監聽器
在ExpandableListView中並沒有提供設置子項長按監聽器的方法,多方查找之後找到了一個算是比較靠譜的方法,目前用起來暫時還沒有什麼問題,有問題再來更新這篇文章。
效果圖:
ExpandableListView中關於長按有一個setOnItemLongClickListener方法,但是這個方法有個問題,沒辦法區分被長按的item是父項還是子項,所以需要在自定義adapter的getGroupView方法和getChildView方法中加一點東西來區分是父項還是子項:
完整的getGroupView方法和getChildView方法:
-
- @Override
- public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity
- .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = inflater.inflate(R.layout.parent_item, null);
- }
- view.setTag(R.layout.parent_item, parentPos);
- view.setTag(R.layout.child_item, -1);
- TextView text = (TextView) view.findViewById(R.id.parent_title);
- text.setText(parentList[parentPos]);
- return view;
- }
-
-
- @Override
- public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {
- if (view == null) {
- LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity
- .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = inflater.inflate(R.layout.child_item, null);
- }
- view.setTag(R.layout.parent_item, parentPos);
- view.setTag(R.layout.child_item, childPos);
- TextView text = (TextView) view.findViewById(R.id.child_title);
- text.setText(dataset.get(parentList[parentPos]).get(childPos));
- text.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Toast.makeText(ExpandableListViewTestActivity.this, "點到了內置的textview",
- Toast.LENGTH_SHORT).show();
- }
- });
- return view;
- }
這裏用到了view的setTag方法,一共設置了兩個Tag,標籤雖然在設置的時候提示說只要int類型即可,但一開始使用0和1來做tag的時候,顯示沒有報錯,但編譯運行就報錯了,要求是資源文件的id才行,因此換成了R.layout.parent_item和R.layout.child_item。
如果是父項,就設置R.layout.parent_item爲第幾個父項,設置R.layout.child_item爲-1。如果是子項,就設置R.layout.parent_item屬於第幾個父項,設置R.layout.child_item爲該父項的第幾個子項,這樣就可以區分被長按的是父項還是子項了。
然後設置ExpandableListView長按item的監聽器:
- listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
- String content = "";
- if ((int) view.getTag(R.layout.child_item) == -1) {
- content = "父類第" + view.getTag(R.layout.parent_item) + "項" + "被長按了";
- } else {
- content = "父類第" + view.getTag(R.layout.parent_item) + "項" + "中的"
- + "子類第" + view.getTag(R.layout.child_item) + "項" + "被長按了";
- }
- Toast.makeText(ExpandableListViewTestActivity.this, content, Toast.LENGTH_SHORT).show();
- return true;
- }
- });
這樣就可以區分父項和子項了。
3、更新數據
列表項更新數據一般使用的都是adapter的notifyDataSetChanged()方法,ExpandableListView也不例外,下面展示一下怎麼更新數據。
效果圖:
首先要在原先的expandable_layout.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"
- android:orientation="vertical">
- <ExpandableListView
- android:id="@+id/expandablelistview"
- android:layout_margin="5dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <Button
- android:id="@+id/updateData"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:layout_gravity="center"
- android:text="刷新數據"/>
- </LinearLayout>
然後在Activity文件中引入這個Button:
- button = (Button) findViewById(R.id.updateData);
爲button設置點擊的監聽器:
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- updateData();
- Toast.makeText(ExpandableListViewTestActivity.this, "數據已更新", Toast.LENGTH_SHORT).show();
- }
- });
其中updateData()方法就是用於更新ExpandableListView的方法,看一下具體的實現:
-
-
-
- private void updateData() {
- childrenList1.clear();
- childrenList1.add(parentList[0] + "-new-" + "first");
- childrenList1.add(parentList[0] + "-new-" + "second");
- childrenList1.add(parentList[0] + "-new-" + "third");
- childrenList2.clear();
- childrenList2.add(parentList[1] + "-new-" + "first");
- childrenList2.add(parentList[1] + "-new-" + "second");
- childrenList2.add(parentList[1] + "-new-" + "third");
- childrenList3.clear();
- childrenList3.add(parentList[2] + "-new-" + "first");
- childrenList3.add(parentList[2] + "-new-" + "second");
- childrenList3.add(parentList[2] + "-new-" + "third");
- adapter.notifyDataSetChanged();
- }
還記得上面初始化數據的時候使用的childrenList1、childrenList2、childrenList3嗎,這三個列表是我們用來放子項列表,然後已經添加到dataset裏面去了。這個時候無需再用dataset添加一次,只需要將列表清空,添加上更新的內容即可。也可以根據自己的需求選擇性地刪除一些東西來獲得新的子項列表。
需要注意的是,子項列表在內存中的地址是不可以改變的,不能使用形如childrenList1 = new ArrayList<>();這樣的方法來獲的新列表,這樣會使childrenList1在內存中的地址發生改變,導致調用adapter的notifyDataSetChanged()方法時不生效。所以如果想要清空這個列表項時,使用childrenList1.clear()方法,這樣才能保證順利更新列表。
文章中使用的代碼源碼已放上github:https://github.com/sysukehan/AndroidTests.git,存放在ExpandableListViewTest模塊下
關於ExpandableListView的使用方法先說到這裏,以後如果有問題或者有東西要補充再更新這篇文章