Android中ExpandableListView的使用

相關文章:

Android中ExpandableListView的使用(二)

ExpandableListView是可擴展的下拉列表,它的可擴展性在於點擊父item可以拉下或收起列表,適用於一些場景的使用,下面介紹的是在Activity中如何使用,關於它的各種樣式的詳細解釋請見另一篇文章:Android中ExpandableListView常用屬性總結

下面介紹它的基本使用方法

先看一下效果:


一、最基本的使用

新建一個佈局文件expandable_layout.xml,內容很簡單,一個LinearLayout裏面包含了一個ExpandableListView,別忘了給它加上id:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical">  
  6.     <ExpandableListView  
  7.         android:id="@+id/expandablelistview"  
  8.         android:layout_margin="5dp"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content" />  
  11. </LinearLayout>  

然後在activity文件中引入這個layout,獲取這個ExpandableListView:

[java] view plain copy
  1. private ExpandableListView listview;  
[java] view plain copy
  1. setContentView(R.layout.expandable_layout);  
  2. listview = (ExpandableListView) findViewById(R.id.expandablelistview);  
爲了給ExpandableListView提供數據,需要先初始化數據,這裏使用一個Map來存放數據,類型爲<String, List<String>>:
[java] view plain copy
  1. private Map<String, List<String>> dataset = new HashMap<>();  
  2. private String[] parentList = new String[]{"first""second""third"};  
  3. private List<String> childrenList1 = new ArrayList<>();  
  4. private List<String> childrenList2 = new ArrayList<>();  
  5. private List<String> childrenList3 = new ArrayList<>();  
[java] view plain copy
  1. private void initialData() {  
  2.     childrenList1.add(parentList[0] + "-" + "first");  
  3.     childrenList1.add(parentList[0] + "-" + "second");  
  4.     childrenList1.add(parentList[0] + "-" + "third");  
  5.     childrenList2.add(parentList[1] + "-" + "first");  
  6.     childrenList2.add(parentList[1] + "-" + "second");  
  7.     childrenList2.add(parentList[1] + "-" + "third");  
  8.     childrenList3.add(parentList[2] + "-" + "first");  
  9.     childrenList3.add(parentList[2] + "-" + "second");  
  10.     childrenList3.add(parentList[2] + "-" + "third");  
  11.     dataset.put(parentList[0], childrenList1);  
  12.     dataset.put(parentList[1], childrenList2);  
  13.     dataset.put(parentList[2], childrenList3);  
  14. }  
然後需要自己實現一個Adapte類,用於爲ExpandableListView提供數據,該類繼承了BaseExpandableListAdapter,下面這個是最簡單的自定義的類:
[java] view plain copy
  1. private class MyExpandableListViewAdapter extends BaseExpandableListAdapter {  
  2.   
  3.     //  獲得某個父項的某個子項  
  4.     @Override  
  5.     public Object getChild(int parentPos, int childPos) {  
  6.         return dataset.get(parentList[parentPos]).get(childPos);  
  7.     }  
  8.   
  9.     //  獲得父項的數量  
  10.     @Override  
  11.     public int getGroupCount() {  
  12.         return dataset.size();  
  13.     }  
  14.   
  15.     //  獲得某個父項的子項數目  
  16.     @Override  
  17.     public int getChildrenCount(int parentPos) {  
  18.         return dataset.get(parentList[parentPos]).size();  
  19.     }  
  20.   
  21.     //  獲得某個父項  
  22.     @Override  
  23.     public Object getGroup(int parentPos) {  
  24.         return dataset.get(parentList[parentPos]);  
  25.     }  
  26.   
  27.     //  獲得某個父項的id  
  28.     @Override  
  29.     public long getGroupId(int parentPos) {  
  30.         return parentPos;  
  31.     }  
  32.   
  33.     //  獲得某個父項的某個子項的id  
  34.     @Override  
  35.     public long getChildId(int parentPos, int childPos) {  
  36.         return childPos;  
  37.     }  
  38.   
  39.     //  按函數的名字來理解應該是是否具有穩定的id,這個方法目前一直都是返回false,沒有去改動過  
  40.     @Override  
  41.     public boolean hasStableIds() {  
  42.         return false;  
  43.     }  
  44.   
  45.     //  獲得父項顯示的view  
  46.     @Override  
  47.     public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {  
  48.         return view;  
  49.     }  
  50.   
  51.     //  獲得子項顯示的view  
  52.     @Override  
  53.     public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {  
  54.         return view;  
  55.     }  
  56.   
  57.     //  子項是否可選中,如果需要設置子項的點擊事件,需要返回true  
  58.     @Override  
  59.     public boolean isChildSelectable(int i, int i1) {  
  60.         return false;  
  61.     }  
  62.  }  

其中註釋說明了每個方法的作用,這個adapter的所有數據來源都是剛剛初始化的dataset,因此當這個adapter需要返回父項的數目時,返回的就是dataset的大小,如果需要返回某個父項的某個子項時,通過父項的position,使用map的get方法即可獲得。自定義的類中最重要的是下面這兩個方法,下面先介紹getGroupView方法:

[java] view plain copy
  1. //  獲得父項顯示的view  
  2. @Override  
  3. public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {  
  4.     if (view == null) {  
  5.         LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity  
  6.                 .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  7.         view = inflater.inflate(R.layout.parent_item, null);  
  8.     }  
  9.     view.setTag(R.layout.parent_item, parentPos);  
  10.     view.setTag(R.layout.child_item, -1);  
  11.     TextView text = (TextView) view.findViewById(R.id.parent_title);  
  12.     text.setText(parentList[parentPos]);  
  13.     return view;  
  14. }  

這個方法用來指定父項顯示的樣式,內容和行爲,其中使用了parent_item佈局:
[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent">  
  5.     <TextView  
  6.         android:id="@+id/parent_title"  
  7.         android:layout_width="match_parent"  
  8.         android:layout_height="wrap_content"  
  9.         android:textSize="20sp"  
  10.         android:textColor="@color/black"  
  11.         android:textStyle="bold"  
  12.         android:text="這是父item"  
  13.         android:layout_margin="5dp"/>  
  14. </LinearLayout>  
佈局很簡單,只是一個LinearLayout包含了一個TextView。

getGroupView方法先判斷view是否爲空,如果view不爲空,說明已經加載過一次parent_item佈局,因此不需要重複加載以提高效率。如果view爲空,那麼使用如下方法加載parent_item佈局:

[java] view plain copy
  1. if (view == null) {  
  2.     LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity  
  3.                    .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  4.     view = inflater.inflate(R.layout.parent_item, null);  
  5. }  

然後定義父項的內容和行爲,這裏只需要定義父項的內容,先通過id獲得parent_item佈局中的TextView,然後通過方法提供的parentPos參數獲取到存放在dataset中的內容,調用TextView的setText方法即可設置父項要顯示的內容:
  1. TextView text = (TextView) view.findViewById(R.id.parent_title);  
  2. text.setText(parentList[parentPos]);  

然後將view返回即可。

接下來是getChildView方法:

[java] view plain copy
  1. //  獲得子項顯示的view  
  2. @Override  
  3. public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {  
  4.     if (view == null) {  
  5.         LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity  
  6.                 .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  7.         view = inflater.inflate(R.layout.child_item, null);  
  8.     }  
  9.     view.setTag(R.layout.parent_item, parentPos);  
  10.     view.setTag(R.layout.child_item, childPos);  
  11.     TextView text = (TextView) view.findViewById(R.id.child_title);  
  12.     text.setText(dataset.get(parentList[parentPos]).get(childPos));  
  13.     text.setOnClickListener(new View.OnClickListener() {  
  14.         @Override  
  15.         public void onClick(View view) {  
  16.             Toast.makeText(ExpandableListViewTestActivity.this"點到了內置的textview", Toast.LENGTH_SHORT).show();  
  17.         }  
  18.     });  
  19.     return view;  
  20. }  
這個方法用於定義子項的佈局,內容和行爲,同樣需要一個child_item來指定子項的佈局:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent">  
  5.     <TextView  
  6.         android:id="@+id/child_title"  
  7.         android:layout_width="wrap_content"  
  8.         android:layout_height="wrap_content"  
  9.         android:textSize="18sp"  
  10.         android:textColor="@color/black"  
  11.         android:text="這是子item"  
  12.         android:layout_margin="5dp"/>  
  13. </LinearLayout>  
關於view的處理和父項一樣,這裏在方法中還給子項顯示內容的textview添加了一個點擊的監聽器。

adapter自定義完之後,通過聲明一個Adapter,實例化後調用setAdapter方法即可:

[java] view plain copy
  1. private MyExpandableListViewAdapter adapter;  
[java] view plain copy
  1. adapter = new MyExpandableListViewAdapter();  
  2. listview.setAdapter(adapter);  
至此,最基本的ExpandableListView的使用就可以滿足啦

二、稍微複雜一點的使用方法

1、爲子項添加點擊的監聽事件,效果圖:


只需要調用ExpandableListView的setOnChildClickListener方法即可:

[java] view plain copy
  1. listview.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {  
  2.     @Override  
  3.     public boolean onChildClick(ExpandableListView expandableListView, View view,  
  4.                                 int parentPos, int childPos, long l) {  
  5.         Toast.makeText(ExpandableListViewTestActivity.this,  
  6.                 dataset.get(parentList[parentPos]).get(childPos), Toast.LENGTH_SHORT).show();  
  7.         return true;  
  8.     }  
  9. });  

這裏在每個子項被點擊了之後會顯示是哪個子項被點擊了
特別注意
(1)在使用這個方法的時候需要將自定義的adapter中的isChildSelectable方法的返回值設置爲true,否則子項的點擊不生效,但子項佈局中設置的控件的監聽器依然可以生效。
  1. //  子項是否可選中,如果需要設置子項的點擊事件,需要返回true  
  2. @Override  
  3. public boolean isChildSelectable(int i, int i1) {  
  4.     return true;  
  5. }  
(2)如果在子項中對某個控件設置了監聽器,這個控件要注意不能鋪滿整個子項,所以在設置高度和寬度時要特別注意,否則設置了子項的監聽器也是沒有用的

2、爲子項添加長按的監聽器
在ExpandableListView中並沒有提供設置子項長按監聽器的方法,多方查找之後找到了一個算是比較靠譜的方法,目前用起來暫時還沒有什麼問題,有問題再來更新這篇文章。
效果圖:

ExpandableListView中關於長按有一個setOnItemLongClickListener方法,但是這個方法有個問題,沒辦法區分被長按的item是父項還是子項,所以需要在自定義adapter的getGroupView方法和getChildView方法中加一點東西來區分是父項還是子項:
完整的getGroupView方法和getChildView方法:
  1. //  獲得父項顯示的view  
  2. @Override  
  3. public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup) {  
  4.     if (view == null) {  
  5.         LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity  
  6.                 .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  7.         view = inflater.inflate(R.layout.parent_item, null);  
  8.     }  
  9.     view.setTag(R.layout.parent_item, parentPos);  
  10.     view.setTag(R.layout.child_item, -1);  
  11.     TextView text = (TextView) view.findViewById(R.id.parent_title);  
  12.     text.setText(parentList[parentPos]);  
  13.     return view;  
  14. }  
  15.   
  16. //  獲得子項顯示的view  
  17. @Override  
  18. public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup) {  
  19.     if (view == null) {  
  20.         LayoutInflater inflater = (LayoutInflater) ExpandableListViewTestActivity  
  21.                 .this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  22.         view = inflater.inflate(R.layout.child_item, null);  
  23.     }  
  24.     view.setTag(R.layout.parent_item, parentPos);  
  25.     view.setTag(R.layout.child_item, childPos);  
  26.     TextView text = (TextView) view.findViewById(R.id.child_title);  
  27.     text.setText(dataset.get(parentList[parentPos]).get(childPos));  
  28.     text.setOnClickListener(new View.OnClickListener() {  
  29.         @Override  
  30.         public void onClick(View view) {  
  31.             Toast.makeText(ExpandableListViewTestActivity.this"點到了內置的textview",  
  32.                     Toast.LENGTH_SHORT).show();  
  33.         }  
  34.     });  
  35.     return view;  
  36. }  
這裏用到了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的監聽器:
  1. listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {  
  2.             @Override  
  3.             public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {  
  4.                 String content = "";  
  5.                 if ((int) view.getTag(R.layout.child_item) == -1) {  
  6.                     content = "父類第" + view.getTag(R.layout.parent_item) + "項" + "被長按了";  
  7.                 } else {  
  8.                     content = "父類第" + view.getTag(R.layout.parent_item) + "項" + "中的"  
  9.                             + "子類第" + view.getTag(R.layout.child_item) + "項" + "被長按了";  
  10.                 }  
  11.                 Toast.makeText(ExpandableListViewTestActivity.this, content, Toast.LENGTH_SHORT).show();  
  12.                 return true;  
  13.             }  
  14.  });  
這樣就可以區分父項和子項了。
3、更新數據
列表項更新數據一般使用的都是adapter的notifyDataSetChanged()方法,ExpandableListView也不例外,下面展示一下怎麼更新數據。
效果圖:

首先要在原先的expandable_layout.xml文件中添加一個按鈕,用來更新數據:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical">  
  6.     <ExpandableListView  
  7.         android:id="@+id/expandablelistview"  
  8.         android:layout_margin="5dp"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content" />  
  11.     <Button  
  12.         android:id="@+id/updateData"  
  13.         android:layout_width="wrap_content"  
  14.         android:layout_height="wrap_content"  
  15.         android:layout_margin="10dp"  
  16.         android:layout_gravity="center"  
  17.         android:text="刷新數據"/>  
  18. </LinearLayout>  
然後在Activity文件中引入這個Button:
  1. private Button button;  
  1. button = (Button) findViewById(R.id.updateData);  
爲button設置點擊的監聽器:
  1. button.setOnClickListener(new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View view) {  
  4.         updateData();  
  5.         Toast.makeText(ExpandableListViewTestActivity.this"數據已更新", Toast.LENGTH_SHORT).show();  
  6.     }  
  7. });  
其中updateData()方法就是用於更新ExpandableListView的方法,看一下具體的實現:
  1. /** 
  2.  * 更新數據 
  3.  */  
  4. private void updateData() {  
  5.     childrenList1.clear();  
  6.     childrenList1.add(parentList[0] + "-new-" + "first");  
  7.     childrenList1.add(parentList[0] + "-new-" + "second");  
  8.     childrenList1.add(parentList[0] + "-new-" + "third");  
  9.     childrenList2.clear();  
  10.     childrenList2.add(parentList[1] + "-new-" + "first");  
  11.     childrenList2.add(parentList[1] + "-new-" + "second");  
  12.     childrenList2.add(parentList[1] + "-new-" + "third");  
  13.     childrenList3.clear();  
  14.     childrenList3.add(parentList[2] + "-new-" + "first");  
  15.     childrenList3.add(parentList[2] + "-new-" + "second");  
  16.     childrenList3.add(parentList[2] + "-new-" + "third");  
  17.     adapter.notifyDataSetChanged();  
  18. }  
還記得上面初始化數據的時候使用的childrenList1、childrenList2、childrenList3嗎,這三個列表是我們用來放子項列表,然後已經添加到dataset裏面去了。這個時候無需再用dataset添加一次,只需要將列表清空,添加上更新的內容即可。也可以根據自己的需求選擇性地刪除一些東西來獲得新的子項列表。
需要注意的是,子項列表在內存中的地址是不可以改變的,不能使用形如childrenList1 = new ArrayList<>();這樣的方法來獲的新列表,這樣會使childrenList1在內存中的地址發生改變,導致調用adapter的notifyDataSetChanged()方法時不生效。所以如果想要清空這個列表項時,使用childrenList1.clear()方法,這樣才能保證順利更新列表。

文章中使用的代碼源碼已放上github:https://github.com/sysukehan/AndroidTests.git,存放在ExpandableListViewTest模塊下
關於ExpandableListView的使用方法先說到這裏,以後如果有問題或者有東西要補充再更新這篇文章
發佈了41 篇原創文章 · 獲贊 10 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章