android开发中常常需要使用到ExpandableListView来对一组数据进行分组, 使用ExpandableListView能够达到类似QQ分组的效果,类似于下面两张效果图:
要达到这种效果其实并不难,因为ExpandableListView跟ListView很相似,如果你对ListView很熟悉的话,很快就能掌握它,如果对ListView不熟悉的话,建议 先看看之前的博文 android学习之ListView的基本使用
首先定义一个主布局:activity_main.xml
<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" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<ExpandableListView
android:id="@+id/expandableView"
android:layout_width="wrap_content"
android:layout_height="wrap_content">ExpandableListView>
RelativeLayout>
然后自定义ExpandableListView中的组名显示布局:item_group.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_group_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true"
android:text="title"
android:textSize="30sp"
android:layout_marginLeft="30dp"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/iv_group_img"
android:layout_alignParentRight="true"/>
RelativeLayout>
接着定义每个组下子项的布局:item_child.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_child_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="30dp"
android:textSize="20sp"
android:layout_alignParentLeft="true"
android:text="test"/>
<TextView
android:id="@+id/tv_child_explain"
android:layout_width="wrap_content"
android:layout_alignLeft="@id/tv_child_title"
android:layout_below="@id/tv_child_title"
android:layout_height="wrap_content"
android:text="explain"/>
<ImageView
android:id="@+id/iv_child_img"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
RelativeLayout>
为了方便,可以建立一个类为每个布局的组件创建一个字段,如给子项定义一个Child类:
/**
* Created by mhwang on 2015/11/25.
*/
public class Child {
/**子项标题*/
private String title;
/**子项解释*/
private String explain;
public Child(){
title = "title";
explain = "explain";
}
public String getTitle() {
return title;
}
public String getExplain() {
return explain;
}
public void setTitle(String title) {
this.title = title;
}
public void setExplain(String explain) {
this.explain = explain;
}
}
如果需要,也可以为群组建立类。
然后到了关键部分,为ExpandableListView创建一个适配器,适配器是连接视图与数据的桥梁。负责将数据送到视图中。这里自定义MyAdapter适配器继承BaseExpandableListAdapter,并重写其所有方法。
/**
* Created by mhwang on 2015/11/25.
*/
public class MyAdapter extends BaseExpandableListAdapter {
/**群组名*/
private ArrayList groups;
/**群组下的子项*/
private ArrayList> childs;
private Context mContext;
public MyAdapter(Context context){
mContext = context;
init();
}
/**为了测试方便,先初始化一些数据*/
public void init(){
groups = new ArrayList();
//添加一些群组
for(int i = 0; i < 10; i++){
groups.add("群组"+i);
}
childs = new ArrayList>();
//为每个群组添加一些子项
for(int i = 0; i < groups.size(); i++){
//如果该群组是偶数,为其添加5个子项,奇数添加3项
ArrayList values = new ArrayList<>();
if(i % 2 == 0 ){
for(int j = 0; j < 5; j++) {
Child child = new Child();
child.setTitle("子项"+j);
child.setExplain("解释"+j);
values.add(child);
childs.add(values);
}
}else{
for(int j = 0; j < 3; j++){
Child child = new Child();
child.setTitle("子项"+j);
child.setExplain("解释"+j);
values.add(child);
childs.add(values);
}
}
}
}
@Override
public int getGroupCount() {
return groups.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return childs.get(groupPosition).size();
}
@Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return childs.get(groupPosition).get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
/**创建view */
View v = LayoutInflater.from(mContext).inflate(R.layout.item_group,null);
/**通过创建的view与自定义布局绑定,就要以找到布局里的组件了*/
TextView tvGroupTitle = (TextView)v.findViewById(R.id.tv_group_title);
ImageView ivGroupImg = (ImageView)v.findViewById(R.id.iv_group_img);
tvGroupTitle.setText(groups.get(groupPosition));
ivGroupImg.setBackgroundResource(R.drawable.m1);
return v;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View v = LayoutInflater.from(mContext).inflate(R.layout.item_child,null);
TextView tvChildTitle = (TextView)v.findViewById(R.id.tv_child_title);
TextView tvChildExplain = (TextView)v.findViewById(R.id.tv_child_explain);
ImageView ivChildImg = (ImageView)v.findViewById(R.id.iv_child_img);
tvChildTitle.setText(childs.get(groupPosition).get(childPosition).getTitle());
tvChildExplain.setText(childs.get(groupPosition).get(childPosition).getExplain());
ivChildImg.setBackgroundResource(R.drawable.m2);
return v;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
最后一步,在MainActivity中获取ExpandableListView实例并调用其setAdapter方法将自定义的MyAdapter对象传进去即可。
public class MainActivity extends AppCompatActivity {
ExpandableListView expandableListView;
MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
expandableListView = (ExpandableListView)findViewById(R.id.expandableView);
adapter = new MyAdapter(this);
expandableListView.setAdapter(adapter);
}
}
这里有几点值得注意:
1、与ListView不同,在ExpandableListView中使用ViewHolder的话会出问题,因为ExpandableListView中的每一个组下子项数目并不是相同的,重用之前的convertView会崩溃。
2、如果是从外部读入数据到列表项中,如数据库等,要特别注意列表项数据为null的情形,否则会有空指针异常并崩溃。