前些日子项目中须要实现一个相似于智联招聘的专业选择页面,简单地说就是点击一级专业列表中的某一项就会展开二级专业列表,一级列表就是一个个组(组选项),二级列表就是一个组里面的成员(子选项)。智联招聘的效果以下: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包下面这几个文件就好了:
我也将本项目的代码和资源文件打包上传到了百度网盘,你能够直接下载使用:
百度网盘
事实上,这里代码难度不大,因此我强烈建议你亲自动手敲一遍。最后,祝你们学习愉快。