今天有点儿闲,闲得无聊就写了个ExpandableListView。。现在recyclerView逐渐已经代替ListView了。不知道ExpandableListView还能有多远。
首先介绍一下ExpandableListView吧:
常见使用场景: qq好友列表,各种分组。点击某个分组展开查看子条目。
实现方法:和Listview一样,在布局中使用ExpandableListView。然后自定义适配器,继承BaseExpandableListAdapter。
布局很简单 就是只有一个ExpandableListView
<RelativeLayout 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"
tools:context="${relativePackage}.${activityClass}" >
<ExpandableListView
android:id="@+id/expandableLv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:groupIndicator="@null" />
RelativeLayout>
安卓系统会默认显示小箭头,用来展示当前的父条目是否展开,如果想去掉默认的小箭头,添加
android:groupIndicator=”@null”属性即可。
自定义Adapter继承BaseExpandableListAdapter
public class MyAdapter extends BaseExpandableListAdapter {
private Context context;
private List> childList;// 子条目
private ArrayList parentList;// 父条目
public MyAdapter(Context context, List> childList,
ArrayList parentList) {
super();
this.context = context;
this.childList = childList;
this.parentList = parentList;
}
/**
* 返回父条目的个数
*/
@Override
public int getGroupCount() {
// TODO Auto-generated method stub
return parentList.size();
}
/**
* 返回当前父条目中子条目的个数 展开子条目时调用
*/
@Override
public int getChildrenCount(int groupPosition) {
// TODO Auto-generated method stub
return childList.get(groupPosition).size();
}
/**
* 返回父条目对象
*/
@Override
public Object getGroup(int groupPosition) {
// TODO Auto-generated method stub
return parentList.get(groupPosition);
}
/**
* 返回每一个子条目对象(与要显示的内容无关)
*/
@Override
public Object getChild(int groupPosition, int childPosition) {
// TODO Auto-generated method stub
return childList.get(groupPosition).get(childPosition);
}
/**
* 返回父条目的id 默认使用索引值
*/
@Override
public long getGroupId(int groupPosition) {
// TODO Auto-generated method stub
return groupPosition;
}
/**
* 返回子条目的id 默认使用索引值
*/
@Override
public long getChildId(int groupPosition, int childPosition) {
// TODO Auto-generated method stub
return childPosition;
}
@Override
public boolean hasStableIds() {
// TODO Auto-generated method stub
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
ParentViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(context, R.layout.exlv_parent, null);
holder = new ParentViewHolder();
holder.tv = (TextView) convertView.findViewById(R.id.tv_parent);
convertView.setTag(holder);
} else {
holder = (ParentViewHolder) convertView.getTag();
}
holder.tv.setText(parentList.get(groupPosition).tittle);
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
ChildViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(context, R.layout.exlv_children, null);
holder = new ChildViewHolder();
holder.iv = (ImageView) convertView.findViewById(R.id.iv_child);
holder.tv = (TextView) convertView.findViewById(R.id.tv_child);
convertView.setTag(holder);
} else {
holder = (ChildViewHolder) convertView.getTag();
}
holder.tv
.setText(childList.get(groupPosition).get(childPosition).tittle);
return convertView;
}
/**
* 子条目是否可以选中
*/
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
// TODO Auto-generated method stub
return true;
}
public class ChildViewHolder {
ImageView iv;
TextView tv;
}
public class ParentViewHolder {
TextView tv;
}
}
传参方式也同ListView。使用构造方法把组条目要显示的数据和子条目的数据传到适配器中。数据类型不限,可以是数组、map、list。需要重写的方法相对于BaseAdapter差不多,多了一些特殊的方法,看方法名就很容易理解。代码中也添加了注释。
这里的父条目的布局只有一个Textview,子条目的布局包含一个ImageView和一个TextView。
创建数据源:
parentList = new ArrayList();
for (int i = 0; i < 5; i++) {
parentList.add(new ParentItemBean("第" + i + "条父条目", i));
}
childList = new ArrayList>();
for (int i = 0; i < 4; i++) {
ArrayList list = new ArrayList();
if (i % 2 == 0) {
list.add(new ChildItemBean("", "哈哈哈" + i));
list.add(new ChildItemBean("", "hhhh" + i));
} else {
list.add(new ChildItemBean("", "这就" + i));
list.add(new ChildItemBean("", "子条目有" + i));
list.add(new ChildItemBean("", "你好吗" + i));
list.add(new ChildItemBean("", "呀咿呀咿呀有有" + i));
}
childList.add(list);
}
childList.add(new ArrayList());
子条目的数据源的类型为
ArrayList<ArrayList<ChildItemBean>> childList;
每一个子条目都放在一个条目组中作为父条目的展开项。
这里需要注意的是:父条目的条数与子条目的所在的集合的条数要一致,如果父条目的条数大于子条目的组数(比如有7个父条目,只有6组子条目),当点击多余的条目时,就会出现越界异常。对于上面的例子来说,就是childlist的数量于parentList的大小相等。那如果要实现父条目中无展开数据数据肿么办?传空的集合即可。也就是上面代码的最后一行,创建一个空的子条目集合。点击父条目后,无展开数据,并且不会报错。
exLv = (ExpandableListView) this.findViewById(R.id.expandableLv);
MyAdapter adapter = new MyAdapter(this, childList, parentList);
exLv.setAdapter(adapter);
exLv.setOnChildClickListener(new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
// TODO Auto-generated method stub
Toast.makeText(
MainActivity.this,
"父级条目:" + groupPosition + " 子条目:" + childPosition
+ " id:" + id, 1).show();
return false;
}
});
exLv.setOnGroupClickListener(new OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this,
"父级条目:" + groupPosition + " id:" + id, 1).show();
return false;
}
});
ExpandableListView还提供了父条目和子条目的点击事件监听。分别是OnGroupClickListener和OnChildClickListener。
设置展开某个父条目:
// 展开某个父条目
exLv.expandGroup(3);
设置展关闭某个父条目:
// 关闭某个父条目
exLv.collapseGroup(3);
网上查了一些修改指示箭头状态的方法,有人说通过自定义selector,讲系统默认的indicator使用的图片资源修改为自定义的selector即可。
但是我试了一下,在selector中没有找到android: state_expanded=”false” 这条属性。所以这种方法暂时是不可取的。。
还有一种方法,隐藏系统默认的箭头,使用imageView代替。然后根据当前父条目的展开状态来填充不同的图片。
首先设置系统默认的indicator的背景为空:在ExpandableListView中设置以下属性:
android:groupIndicator="@null"
然后在Adaptor的getGroupView方法中,根据展开状态来设置不同的图片资源。
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
ParentViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(context, R.layout.exlv_parent, null);
holder = new ParentViewHolder();
holder.tv = (TextView) convertView.findViewById(R.id.tv_parent);
holder.iv_indicator = (ImageView) convertView
.findViewById(R.id.iv_Indicator);
convertView.setTag(holder);
} else {
holder = (ParentViewHolder) convertView.getTag();
}
// 根据当前父条目的展开状态来设置不同的图片
if (isExpanded) {
// 条目展开,设置向下的箭头
holder.iv_indicator.setImageDrawable(context.getResources()
.getDrawable(R.drawable.jiantou2));
} else {
// 条目未展开,设置向上的箭头
holder.iv_indicator.setImageDrawable(context.getResources()
.getDrawable(R.drawable.jiantou));
return convertView;