前些日子項目中須要實現一個相似於智聯招聘的專業選擇頁面,簡單地說就是點擊一級專業列表中的某一項就會展開二級專業列表,一級列表就是一個個組(組選項),二級列表就是一個組裏面的成員(子選項)。智聯招聘的效果以下:html
如今的主流列表控件毫無疑問是RecyclerView
,因此你也許會想到用一個RecyclerView來顯示組列表,而後在其item裏面再嵌套一個RecyclerView顯示子選項列表。點擊組選項就將嵌套的RecyclerView佈局設爲visible
或者gone
來展開和關閉子列表。這種作法有以下的缺點:java
雖然RecyclerView
是當紅炸子雞,可是解決這些問題仍是得老司機ExpandableListView
出馬了。這是有點年頭的控件了,不過寶刀未老,咱們能夠用它輕鬆實現下拉列表效果。在這裏我不打算一一羅列ExpandableListView
的用法,而是採起實戰的方式,以實現需求爲中心,用到哪一個再講那個。由於我以爲在實戰中學習和填坑更有趣味,更有效果。因此,下面咱們就一塊兒來作一個智聯招聘的專業選擇頁面吧。android
先提早看看咱們要實現的效果:git
總體佈局很簡單,放一個ExpandableListView就能夠了:segmentfault
<FrameLayout 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" > <ExpandableListView android:id="@+id/expandable_list" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
組選項只須要顯示文字,因此先放一個TextView:數組
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="50dp" android:paddingLeft="20dp" android:gravity="center_vertical" android:background="@android:color/white" android:orientation="vertical"> <TextView android:text="dd" android:gravity="center_vertical" android:id="@+id/tv_group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textSize="16sp" /> </LinearLayout>
子選項的item佈局與組選項的惟一區別就是它的背景是灰色的(android:background="#F2F2F2"
),代碼就不重複貼了。app
一個組選項對應的是一組子選項,因此組選項的數據是一個一級數組,子選項的數據是二級數組。爲了添加數據方便,我這裏使用的是集合,每一組的子選項數據個數設爲隨機:dom
private void initData(){ //初始化一級專業數據 for (int i = 1; i <= 15; i++) { groupList.add(new StringBuffer("一級專業").append(i).toString()); } //初始化二級專業數據 Random random = new Random(); for (String s : groupList) { List<String> childDatas = new ArrayList<>(); int size = random.nextInt(10) + 5; for (int i = 1; i <= size; i++) { childDatas.add(new StringBuffer("二級專業").append(i).toString()); } childList.add(childDatas); } }
運行以後發現數據都有了,可是見鬼,爲何組選項和子選項的高度都那麼窄呢?ide
這能夠算是ExpandableListView
的一個小坑,當組選項或者子選項的根佈局高度設置爲固定值,實際出來的效果倒是wrap_content
。解決這個問題能夠給根佈局再加一個屬性android:minHeight="50dp"
,或者也給子控件TextView
加上固定的高度(若是你的item裏面的控件比較簡單能夠採起這個方法)。固然,若是你的item高度不是一個固定值,也能夠將高度設爲wrap_content
,而後在裏面設置padding或者margin值,好比:佈局
android:paddingTop="10dp" android:paddingBottom="10dp"
你能夠根據需求採起合適的方案。
仔細觀察咱們能夠發現,ExpandableListView
默認顯示了一個指示器,也就是左邊的小箭頭,而且提供了屬性android:groupIndicator
來設置其樣式。這個指示器是固定在左邊的,雖然能夠設置上下左右的距離,可是很難控制,一不當心就會跟文字重疊,因此我作法是乾脆設置android:groupIndicator="@null"
讓其消失,而後本身在組選項的佈局中用兩張圖片來替代。
組選項的佈局修改以下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="50dp" android:background="@android:color/white" android:gravity="center_vertical" android:minHeight="50dp" android:orientation="vertical" android:paddingLeft="20dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_toLeftOf="@+id/iv_indicator" android:layout_centerVertical="true" android:id="@+id/tv_group" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:textColor="@android:color/black" android:textSize="16sp" /> <ImageView android:layout_marginRight="20dp" android:id="@+id/iv_indicator" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> </LinearLayout>
回到MajorAdapter
,在getGroupView
方法中有一個isExpanded
參數,它表示的是組選項的展開狀態,true時表示展開,false則是閉合。有了它,咱們就能夠輕鬆決定箭頭指示器的方向了。
@Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { …… //根據列表的展開狀態來決定箭頭的方向 groupHolder.ivIndicator.setImageResource(isExpanded ? R.drawable.ic_arrow_up : R.drawable.ic_arrow_down); return convertView; }
ExpandableListView
是繼承於ListView
,因此它也能夠經過android:divider
屬性來同時設置組選項和子選項的分割線。咱們須要的分割線左邊有20dp的間距,因此只好設置android:divider="@null"
來去掉原生的分割線,而且本身寫一條了。
建立一個layout_divider.xml的佈局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="#d4d4d4" android:layout_height="0.5dp" > </RelativeLayout>
而後在組選項和子選項的佈局中include
進去:
<include layout="@layout/layout_divider" />
至此,咱們的界面就已經完成了,如今來實現它的點擊事件吧。
ExpandableListView
提供了setOnGroupClickListener
和setOnChildClickListener
兩個方法來分別監聽組選項和子選項的點擊事件。好比監聽子選項的點擊事件:
//子選項的點擊事件 expandableList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { String toastStr = new StringBuffer("你選擇了").append(groupList.get(groupPosition)) .append("的").append(childList.get(groupPosition).get(childPosition)).toString(); Toast.makeText(context, toastStr, Toast.LENGTH_SHORT).show(); return false; } });
組選項的點擊監聽事件類似,這裏就再也不贅述了。
如今咱們來實現一個智聯招聘中沒有的功能吧,即點擊某一組選項時,被點擊的組選項展開,其餘展開的組選項自動關閉,也就是每次只能有一個組選項展開子列表。你也許會想到用上一節中的setOnGroupClickListener
,可是這個是每次組選項被點擊時都會監聽,哪怕是已經關閉了,因此性能上會有點浪費。除此以外,咱們還有更好的選擇,那就是使用setOnGroupExpandListener
監聽列表的展開事件:
//實現每次只展開一個組選項列表的功能 expandableList.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() { @Override public void onGroupExpand(int groupPosition) { //獲取組選項個數 int groupSize = expandableList.getExpandableListAdapter().getGroupCount(); for (int i = 0; i < groupSize; i++) { if (i != groupPosition && expandableList.isGroupExpanded(i)) { //不是當前點擊的組選項且處於展開狀態的就關閉 expandableList.collapseGroup(i); } } } });
每次點擊展開某個組選項時,咱們就遍歷組選項列表,比較它們的groupPosition
,若是不是當前點擊的組選項且處於展開狀態,就調用collapseGroup
將其關閉。
你必定會想到,既然有方法能夠將某一組選項裏面的列表關閉,是否是也有對應的方法展開呢?沒錯,相對於collapseGroup
,還有對應的expandGroup
方法,並且它的第二個參數能夠設置展開時是否顯示動畫效果。若是你想頁面顯示時就展開某一特定的子列表,那麼就可使用expandGroup
了。
至此,咱們的界面和功能都已經實現完畢了。如今就來梳理用到的屬性和API吧。
首先是xml屬性:
屬性 | 做用 |
---|---|
android:groupIndicator | 設置組選項的指示器 |
android:divider | 設置組選項和子選項列表的分割線 |
而後是用到的方法:
方法 | 做用 |
---|---|
setOnGroupExpandListener | 組選項的點擊監聽事件 |
setOnChildClickListener | 子選項的點擊監聽事件 |
setOnGroupExpandListener | 組選項的展開監聽事件 |
getExpandableListAdapter | 獲取ExpandableListView綁定的Adapter |
collapseGroup | 關閉某一組選項下的列表 |
expandGroup | 展開某一組選項下的列表 |
若是你還想深刻學習ExpandableListView
,能夠閱讀這份官方文檔:
http://www.android-doc.com/reference/android/widget/ExpandableListView.html#getFlatListPosition(long))
源碼我放作了碼雲上,可是因爲裏面還有個人一些亂七八糟的代碼,因此不建議你們把整個工程下載下來,只需關注ExpandableListView包下面這幾個文件就好了:
我也將本項目的代碼和資源文件打包上傳到了百度網盤,你能夠直接下載使用:
百度網盤
事實上,這裏代碼難度不大,因此我強烈建議你親自動手敲一遍。最後,祝你們學習愉快。