ExpandableListView的自定义布局以及注意事项

android开发中常常需要使用到ExpandableListView来对一组数据进行分组, 使用ExpandableListView能够达到类似QQ分组的效果,类似于下面两张效果图:
ExpandableListView的自定义布局以及注意事项_第1张图片
ExpandableListView的自定义布局以及注意事项_第2张图片

要达到这种效果其实并不难,因为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的情形,否则会有空指针异常并崩溃。

你可能感兴趣的:(Android开发)