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

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<String> groups;
    /**群组下的子项*/
    private ArrayList<ArrayList<Child>> childs;
    private Context mContext;
    public MyAdapter(Context context){
        mContext = context;
        init();
    }
    /**为了测试方便,先初始化一些数据*/
    public void init(){
        groups = new ArrayList<String>();
        //添加一些群组
        for(int i = 0; i < 10; i++){
            groups.add("群组"+i);
        }
        childs = new ArrayList<ArrayList<Child>>();
        //为每个群组添加一些子项
        for(int i = 0; i < groups.size(); i++){
            //如果该群组是偶数,为其添加5个子项,奇数添加3项
            ArrayList<Child> 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的情形,否则会有空指针异常并崩溃。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值