Android 两种列表:RecyclerView、ExpandableListView

目录

一、RecyclerView

效果图

代码实现

1.引入recyclerview包

implementation 'com.android.support:design:27.1.0'

2.布局中添加RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_apps"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>

3.MainActivity中设置LayoutManager

public class MainActivity extends AppCompatActivity {

    private RecyclerView rvApps;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rvApps = findViewById(R.id.rv_apps);
        rvApps.setLayoutManager(new LinearLayoutManager(this));
        
    }
}

LayoutManager的作用是表明RecyclerView的展示方式,LinearLayoutManager表示是线性垂直垂直展示。
除了LinearLayout还有GridLayoutManager,StaggeredGridLayoutManager,分别代表网格展示和瀑布流展示。

4.创建RecyclerView适配器

新建MyAdapter,继承自RecyclerView.Adapter:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.mViewHolder>{

    private ArrayList<App> apps;

    //构造方法,传入数据
    public MyAdapter(ArrayList<App> apps) {
        this.apps = apps;
    }

    //初始化控件
    public class mViewHolder extends RecyclerView.ViewHolder {
        private ImageView ivIcon;
        private TextView tvName;
        public mViewHolder(View itemView) {
            super(itemView);
            ivIcon = itemView.findViewById(R.id.iv_icon);
            tvName = itemView.findViewById(R.id.tv_name);
        }
    }

    //绑定布局
    @NonNull
    @Override
    public mViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.app_item,parent,false);
        mViewHolder holder = new mViewHolder(view);
        return holder;
    }

    //对应位置绑定数据,这里可以添加点击事件
    @Override
    public void onBindViewHolder(@NonNull mViewHolder holder, int position) {
        holder.tvName.setText(apps.get(position).getName());
        holder.ivIcon.setImageDrawable(apps.get(position).getIcon());
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //这里添加点击事件
            }
        });
    }

    //获取列表长度
    @Override
    public int getItemCount() {
        return apps.size();
    }
}

里面用到了App类,贴出App类的代码:

public class App {
    private String name;//app名称
    private Drawable icon;//app图标

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Drawable getIcon() {
        return icon;
    }

    public void setIcon(Drawable icon) {
        this.icon = icon;
    }
}

用到的app_item布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:src="@mipmap/ic_launcher"/>
    <TextView
        android:id="@+id/tv_name"
        app:layout_constraintLeft_toRightOf="@+id/iv_icon"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_margin="16dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"/>
</android.support.constraint.ConstraintLayout>

5.为RecyclerView设置适配器

在MainActivity中初始化数据,并为RecyclerView设置adapter,MainActivity最终的代码为:

public class MainActivity extends AppCompatActivity {

    private RecyclerView rvApps;
    private ArrayList<App> apps = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rvApps = findViewById(R.id.rv_apps);
        rvApps.setLayoutManager(new LinearLayoutManager(this));

        //初始化数据
        for (int i = 0;i<50;i++){
            App app = new App();
            app.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
            app.setName(getString(R.string.app_name)+i);
            apps.add(app);
        }

        rvApps.setAdapter(new MyAdapter(apps));
    }
}

以上,便是RecyclerView的基本使用。

二、解决RecyclerView的Item复用导致的问题

1.复用导致的问题

如果RecyclerView的Item中有Checkbox,或者其他的具有记录状态功能的控件。那么很容易引发Item的复用问题

如上图,本来一个很正常的列表,每个item都没被选中,当我选中第一个Checkbox的时候,后面item的Checkbox也变成了选中状态。这就是item的复用导致的错误。

2.问题的原因

由于RecyclerView会自动回收移出屏幕的item,然后把回收回来的item让新进入屏幕的item使用。所以导致了新进入的item保留了之前的选中状态。

3.解决思路

解决item复用问题的思路是:在滑动时虽然checkbox的状态被复用了,但是数据却没有被复用,仍然是1,2,3,4…,所以我们可以通过一个checkStatus列表来保存checkbox的状态,每次选中checkbox时,把该位置加入checkStatus中,每次取消选中checkbox时,把该位置从checkStatus中移除。然后在每次设置checkbox状态的时候根据列表来判断是否选中。

4.代码实现

将MyAdapter修改如下:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.mViewHolder>{

    private List<String> DataList;
    //记录checkbox状态的列表
    private List<Integer> checkStatus;
    //构造方法,传入数据
    public MyAdapter(List<String> DataList) {
        this.DataList = DataList;
    }

    //初始化控件
    public class mViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        private CheckBox checkbox;
        public mViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.text);
            checkbox = itemView.findViewById(R.id.checkbox);
        }
    }

    //绑定布局
    @NonNull
    @Override
    public mViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        mViewHolder holder = new mViewHolder(view);
        return holder;
    }

    //对应位置绑定数据,这里可以添加点击事件
    @Override
    public void onBindViewHolder(@NonNull final mViewHolder holder, int position) {
        if(checkStatus == null){
            checkStatus = new ArrayList<>();
        }
        holder.text.setText(DataList.get(position));
        holder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Integer position = holder.getLayoutPosition();
                if(isChecked&&!checkStatus.contains(position)){
                    //如果checkbox选中了,而且checkStatus中不包含此位置,将该位置加入checkStatus中
                    checkStatus.add(position);
                }else if(!isChecked&&checkStatus.contains(position)){
                    //如果checkbox未选中,而且checkStatus中包含此位置,将该位置从checkStatus移除
                    checkStatus.remove(position);
                }
            }
        });
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.checkbox.setChecked(!holder.checkbox.isChecked());
            }
        });
        //根据checkStatus中是否包含此位置来设置checkbox状态
        holder.checkbox.setChecked(checkStatus.contains(position));
    }

    //获取列表长度
    @Override
    public int getItemCount() {
        return null == DataList ? 0:DataList.size();
    }
}

效果图如下:

三、给RecyclerView添加Head和Foot

效果图

我们先写一个Adapter,然后在此基础上添加foot和head:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private List<String> DataList;
    //构造方法,传入数据
    public MyAdapter(List<String> DataList) {
        this.DataList = DataList;
    }

    //初始化ItemView
    public class ItemViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        public ItemViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.text);
        }
    }

    //绑定布局
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        RecyclerView.ViewHolder holder;
        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        holder = new ItemViewHolder(view);
        return holder;
    }

    //对应位置绑定数据,这里可以添加点击事件
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
        itemViewHolder.text.setText(DataList.get(position-1));
    }

    //获取列表长度
    @Override
    public int getItemCount() {
        return null == DataList ? 0:DataList.size();
    }
}

添加了Head和Foot的RecyclerView的Adapter:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private static final int ITEM = 0;
    private static final int HEAD = 1;
    private static final int FOOT = 2;
    private List<String> DataList;
    //构造方法,传入数据
    public MyAdapter(List<String> DataList) {
        this.DataList = DataList;
    }

    //初始化ItemView
    public class ItemViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        public ItemViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.text);
        }
    }
    //初始化HeadView
    public class HeadViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        public HeadViewHolder(View headView) {
            super(headView);
            text = headView.findViewById(R.id.text);
        }
    }
    //初始化FootView
    public class FootViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        public FootViewHolder(View footView) {
            super(footView);
            text = footView.findViewById(R.id.text);
        }
    }

    //绑定布局
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        RecyclerView.ViewHolder holder;
        switch (viewType){
            case HEAD:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.head,parent,false);
                holder = new HeadViewHolder(view);
                break;
            case FOOT:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.foot,parent,false);
                holder = new FootViewHolder(view);
                break;
            default:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
                holder = new ItemViewHolder(view);
                break;
        }
        return holder;
    }

    //对应位置绑定数据,这里可以添加点击事件
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof HeadViewHolder){
            HeadViewHolder headViewHolder = (HeadViewHolder) holder;
            headViewHolder.text.setText("HEAD");
        }else if(holder instanceof FootViewHolder){
            FootViewHolder footViewHolder = (FootViewHolder) holder;
            footViewHolder.text.setText("FOOT");
        }else if(holder instanceof ItemViewHolder){
            ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
            //比如有3条数据,0是head,1,2,3是item,4是foot。数据下标是0,1,2,所以item绑定position-1对应的数据
            itemViewHolder.text.setText(DataList.get(position-1));
        }
    }

    @Override
    public int getItemViewType(int position) {
        if(position == 0){
            return HEAD;
        } else if(position == getItemCount()-1){
            return FOOT;
        }else{
            return ITEM;
        }
    }

    //获取列表长度
    @Override
    public int getItemCount() {
        return (null == DataList ? 0:DataList.size())+2;
    }
}

原理

添加HEAD和FOOT的原理是:在onCreateViewHolder的参数中,第二个参数ViewType代表View的类型。重写Adapter的getViewType()方法,根据位置返回对应的ViewType。如果位置是0,返回HEAD类型,如果位置是最后一位(getItemCount()-1),返回FOOT类型。其他则返回普通ITEM类型。

代码实现

1.修改getItemCount()

getItemCount()是用来获取列表长度的,在原有基础上+2,表示添加HEAD和FOOT之后的长度

2.修改getViewType()

getViewType()方法中,第一个位置返回HEAD,最后一个位置返回FOOT,其他的返回普通ITEM。

3.添加HeadViewHolder和FootViewHolder

在holder中,绑定Head和Foot的布局并找到控件。这里的布局很简单,只有一个TextView,故不再给出foot和head的布局代码。

4.修改onCreateViewHolder()

在onCreateViewHolder中,根据ViewType创建相应的ViewHolder

5.在onBindViewHolder中绑定数据

使用instance of判断ViewHolder的类型,并给对应的类型绑定数据。这里只是将HeadViewHolder和FootViewHolder中的TextView设置了一个固定的字符串。(复杂的数据可以通过构造方法传进来,类似itemViewHolder绑定数据的写法)

注:RecyclerView的ViewType是我们自己定义的,只要根据getViewType返回不同的ViewType,在创建ViewHolder时候根据ViewType创建不同的ViewHolder,在绑定数据时根据不同的ViewHolder绑定相应数据即可,并不局限于HEAD和FOOT。
例如,可以给奇数位置的Item返回一个ViewType,为其绑定不同的布局。也可以为position%3==0的位置返回一个ViewType,为其绑定不同的布局,非常的灵活。

四、RecyclerView添加FOOT并实现上拉加载更多

效果图

原理

要实现上拉加载更多,需要监听RecyclerView是否滑动到了底部。使用RecyclerView的addOnScrollListener监听RecyclerView的滑动。利用LayoutManager的findLastCompletelyVisibleItemPosition方法找到最后一个完全显示的item的位置。如果显示的是最后一个item,则表示滑动到foot了,执行添加新数据操作。

代码实现

1.先写一个添加了FOOT的Adapter

利用第二节的方法添加的FOOT:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private static final int ITEM = 0;
    private static final int FOOT = 2;
    private List<String> DataList;
    //构造方法,传入数据
    public MyAdapter(List<String> DataList) {
        this.DataList = DataList;
    }

    //初始化ItemView
    public class ItemViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        public ItemViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.text);
        }
    }
    //初始化FootView
    public class FootViewHolder extends RecyclerView.ViewHolder {
        private TextView text;
        public FootViewHolder(View footView) {
            super(footView);
            text = footView.findViewById(R.id.text);
        }
    }

    //绑定布局
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        RecyclerView.ViewHolder holder;
        switch (viewType){
            case FOOT:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.foot,parent,false);
                holder = new FootViewHolder(view);
                break;
            default:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
                holder = new ItemViewHolder(view);
                break;
        }
        return holder;
    }

    //对应位置绑定数据,这里可以添加点击事件
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof FootViewHolder){
            FootViewHolder footViewHolder = (FootViewHolder) holder;
            footViewHolder.text.setText("上拉加载更多");
        }else if(holder instanceof ItemViewHolder){
            ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
            //比如有3条数据,0,1,2是item,3是foot。数据下标是0,1,2,所以item绑定position对应的数据
            itemViewHolder.text.setText(DataList.get(position));
        }
    }

    @Override
    public int getItemViewType(int position) {
        if(position == getItemCount()-1){
            //比如有3条数据,0,1,2是item,3是foot。数据条数为3+1=4,所以用getItemCount()-1
            return FOOT;
        }else{
            return ITEM;
        }
    }

    //获取列表长度
    @Override
    public int getItemCount() {
        return (null == DataList ? 0:DataList.size())+1;
    }
}

2.给RecyclerView添加滑动监听器

MainActivity中的代码:

public class MainActivity extends AppCompatActivity{
    private RecyclerView rvTest;
    private List<String> datas;
    private LinearLayoutManager linearLayoutManager;
    private MyAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rvTest = (RecyclerView) findViewById(R.id.rv_test);
        //初始化数据
        datas = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            datas.add(String.valueOf(i));
        }
        //设置layoutManager
        linearLayoutManager = new LinearLayoutManager(this);
        //设置adapter
        adapter = new MyAdapter(datas);
        //设置滑动监听
        rvTest.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                //如果滑动到foot了,增加新数据
                if(linearLayoutManager.findLastCompletelyVisibleItemPosition() == adapter.getItemCount()-1){
                    for (int i = 0; i < 5; i++) {
                        datas.add(String.valueOf(i));
                        adapter.notifyDataSetChanged();
                    }
                }
            }
        });

        rvTest.setLayoutManager(linearLayoutManager);
        rvTest.setAdapter(adapter);
    }
}

五、利用SwipeRefreshLayout实现下拉刷新

简介

SwipeRefreshLayout是Google官方推出的下拉刷新控件,非常的简单好用。

效果图

代码实现

1.修改布局

在布局中,将RecyclerView用SwipeRefreshLayout包裹起来:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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=".MainActivity">
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_test"
            android:layout_height="match_parent"
            android:layout_width="match_parent"/>
    </android.support.v4.widget.SwipeRefreshLayout>

</android.support.constraint.ConstraintLayout>

2.设置OnRefreshListener

在MainActivity中,找到SwipeRefreshLayout控件,并设置OnRefreshListener即可。

public class MainActivity extends AppCompatActivity{
    private SwipeRefreshLayout swipeRefresh;
    private RecyclerView rvTest;
    private List<String> datas;
    private LinearLayoutManager linearLayoutManager;
    private MyAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
        rvTest = (RecyclerView) findViewById(R.id.rv_test);
        //初始化数据
        datas = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            datas.add(String.valueOf(i));
        }
        //设置layoutManager
        linearLayoutManager = new LinearLayoutManager(this);
        //设置adapter
        adapter = new MyAdapter(datas);
        rvTest.setLayoutManager(linearLayoutManager);
        rvTest.setAdapter(adapter);
        swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                datas.clear();
                for (int i = 0; i < 50; i++) {
                    datas.add(String.valueOf((int)(Math.random()*100)));
                }
                adapter.notifyDataSetChanged();
                swipeRefresh.setRefreshing(false);
            }
        });
    }
}

在OnRefreshListener中执行刷新操作,这里我的刷新操作是将datas重新赋50个随机数值。执行完后调用SwipeRefreshLayout的setRefreshing(false)方法停止刷新。

3.SwipeRefresh可以自定义刷新图标的颜色,通过以下两个方法均可

swipeRefresh.setColorSchemeColors(Color.BLUE,Color.YELLOW,Color.GREEN);
//或者
swipeRefresh.setColorSchemeResources(R.color.colorAccent,R.color.colorPrimary,R.color.colorPrimaryDark);

传入颜色或者颜色资源,数量不限。刷新时将按照设置的多种颜色,每转一圈换一种颜色。

六、ExpandableListView

效果图

代码实现

1.修改布局

在布局中申明ExpandableListView

<ExpandableListView
    android:id="@+id/list"
    android:groupIndicator="@null"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

由于ExpandableListView自带的指示器不是很美观,在布局中设置android:groupIndicator="@null"可以不显示自带的指示器。

另外,ExpandableListView自带的分割线也是可以不显示的,通过属性android:divider="@null"设置。

2.初始化数据

在MainActivity中找到控件并初始化数据:

public class MainActivity extends AppCompatActivity {
    private ExpandableListView list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        list = (ExpandableListView) findViewById(R.id.list);
        //组数据,是一个List<T>
        List<String> groupData = new ArrayList<>();
        groupData.add("小学");
        groupData.add("初中");
        groupData.add("高中");
        //子数据,每一组的子数据是一个List<T>;所以所有组的子数据是一个List<List<T>>
        List<List<String>> childrenData = new ArrayList<>();
        List<String> child1Data = new ArrayList<>();
        child1Data.add("小学一年级");
        child1Data.add("小学二年级");
        child1Data.add("小学三年级");
        child1Data.add("小学四年级");
        child1Data.add("小学五年级");
        child1Data.add("小学六年级");
        List<String> child2Data = new ArrayList<>();
        child2Data.add("初一");
        child2Data.add("初二");
        child2Data.add("初三");
        List<String> child3Data = new ArrayList<>();
        child3Data.add("高一");
        child3Data.add("高二");
        child3Data.add("高三");
        childrenData.add(child1Data);
        childrenData.add(child2Data);
        childrenData.add(child3Data);
    }
}

组数据是一个数据列:

{"小学","初中","高中"}

所以所有的子数据是一列数据列List<List>

{
    {"小学一年级","小学二年级","小学三年级","小学四年级","小学五年级","小学六年级"},
    {"初一","初二","初三"},
    {"高一","高二","高三"}
}

3.创建ExpandableListView的适配器

新建MyExpandableListViewAdapter继承自BaseExpandableListAdapter,并实现需要实现的方法,需要实现十个方法:

代码如下:

public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
    @Override
    public int getGroupCount() {
        return 0;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return 0;
    }

    @Override
    public Object getGroup(int groupPosition) {
        return null;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return null;
    }

    @Override
    public long getGroupId(int groupPosition) {
        return 0;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return 0;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        return null;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        return null;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }
}

4.传入数据

创建MyExpandableListViewAdapter的构造方法,将数据传入,并根据组布局和子布局创建GroupViewHolder和ChildViewHolder,本例的组布局和子布局中只有一个TextView:

public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
    private List<String> groupData;//组数据
    private List<List<String>> childrenData;//子数据
    private Context context;
    public MyExpandableListViewAdapter(List<String> groupData,List<List<String>> childrenData){
        this.groupData = groupData;
        this.childrenData = childrenData;
    }
    public class GroupViewHolder{
        TextView textView;
    }
    public class ChildViewHolder{
        TextView textView;
    }
    ...
}

5.修改继承BaseExpandableListAdapter时实现的十个方法

public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
    private List<String> groupData;//组数据
    private List<List<String>> childrenData;//子数据
    private Context context;
    public MyExpandableListViewAdapter(List<String> groupData,List<List<String>> childrenData){
        this.groupData = groupData;
        this.childrenData = childrenData;
    }

    public class GroupViewHolder{
        TextView textView;
    }
    public class ChildViewHolder{
        TextView textView;
    }
    //获取组数
    @Override
    public int getGroupCount() {
        return groupData.size();
    }
    //获取指定组的子元素数
    @Override
    public int getChildrenCount(int groupPosition) {
        return childrenData.get(groupPosition).size();
    }
    //获取指定组
    @Override
    public Object getGroup(int groupPosition) {
        return groupData.get(groupPosition);
    }
    //获取指定子元素
    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return childrenData.get(groupPosition).get(childPosition);
    }
    //获取组的id
    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }
    //获取子元素的id
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }
    //id是否固定,即数据变化后,数据的id是否改变
    @Override
    public boolean hasStableIds() {
        return false;
    }
    //绑定组布局和组数据
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        context = parent.getContext();
        convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
        GroupViewHolder groupViewHolder = new GroupViewHolder();
        groupViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
        groupViewHolder.textView.setText(groupData.get(groupPosition));
        return convertView;
    }
    //绑定子布局和子数据
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
        ChildViewHolder childViewHolder = new ChildViewHolder();
        childViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
        childViewHolder.textView.setText(childrenData.get(groupPosition).get(childPosition));
        return convertView;
    }
    //子数据是否可选择
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}

方法看似很多,其实非常简单。

1.int getGroupCount()

获取一共有多少组。返回groupData.size()即可

2.int getChildrenCount(int groupPosition)

传入组的位置,获取这一组的子元素数量,返回childrenData.get(groupPosition).size()即可。

3.Object getGroup(int groupPosition)

传入组的位置,获取组数据中这个位置的值。返回groupData.get(groupPosition)即可,注意这里默认返回的类型是Object,使用时可以将返回类型修改成实际的类型,如本例中,组数据中这个位置的值是String类型,所以可以将Object改为String

4.Object getChild(int groupPosition, int childPosition)

传入组的位置和子元素的位置,获取指定组的指定位置的子数据。返回childrenData.get(groupPosition).get(childPosition)即可,与getGroup类似,这里也可以将Object改为String

5.long getGroupId(int groupPosition)

获取组的id,通常返回groupPosition即可

6.long getChildId(int groupPosition, int childPosition)

获取子元素的id,通常返回childPosition即可

7.boolean hasStableIds()

id是否固定,即数据变化后,数据的id是否改变

8.View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)

这个方法用来绑定组的布局和组的数据,使用LayoutInflater将布局绑定到convertView,并返回convertView。使用前面定义的GroupViewHolder为布局中的控件绑定数据。绑定组数据可以使用:

groupViewHolder.textView.setText(groupData.get(groupPosition));

或者是

groupViewHolder.textView.setText(getGroup(groupPosition));

注意这里还有一个参数isExpanded,它代表本组是否是展开状态,可以使用这个参数实现自定义展开和关闭状态的indicator

9.View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)

这个方法用来绑定子元素的布局和子元素的数据,使用LayoutInflater将布局绑定到convertView,并返回convertView。使用前面定义的ChildViewHolder为布局中的控件绑定数据。绑定组数据可以使用:

childViewHolder.textView.setText(childrenData.get(groupPosition).get(childPosition));

或者是

childViewHolder.textView.setText(getChild(groupPosition, childPosition));

10.boolean isChildSelectable(int groupPosition, int childPosition)

子元素是否可选择,需要给子元素添加点击事件的话,这里必须返回true

6.给ExpandableListView添加适配器

MainActivity中,创建适配器,并给ExpandableListView添加适配器。

public class MainActivity extends AppCompatActivity {
    private ExpandableListView list;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        //传入数据,设置适配器
        MyExpandableListViewAdapter adapter = new MyExpandableListViewAdapter(groupData,childrenData);
        list.setAdapter(adapter);
    }
}

7.给ExpandableListView添加点击事件

public class MainActivity extends AppCompatActivity {
    private ExpandableListView list;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        list.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                Toast.makeText(MainActivity.this, "点击了"+adapter.getGroup(groupPosition), Toast.LENGTH_SHORT).show();
                return false;
            }
        });
        list.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                Toast.makeText(MainActivity.this, "点击了"+adapter.getChild(groupPosition,childPosition), Toast.LENGTH_SHORT).show();
                return true;
            }
        });
    }
}

这样就实现了效果图中的效果

附:

1.MainActivity完整代码

public class MainActivity extends AppCompatActivity {
    private ExpandableListView list;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        list = (ExpandableListView) findViewById(R.id.list);
        //组数据,是一个List<T>
        List<String> groupData = new ArrayList<>();
        groupData.add("小学");
        groupData.add("初中");
        groupData.add("高中");
        //子数据,每一组的子数据是一个List<T>;所以所有组的子数据是一个List<List<T>>
        List<List<String>> childrenData = new ArrayList<>();
        List<String> child1Data = new ArrayList<>();
        child1Data.add("小学一年级");
        child1Data.add("小学二年级");
        child1Data.add("小学三年级");
        child1Data.add("小学四年级");
        child1Data.add("小学五年级");
        child1Data.add("小学六年级");
        List<String> child2Data = new ArrayList<>();
        child2Data.add("初一");
        child2Data.add("初二");
        child2Data.add("初三");
        List<String> child3Data = new ArrayList<>();
        child3Data.add("高一");
        child3Data.add("高二");
        child3Data.add("高三");
        childrenData.add(child1Data);
        childrenData.add(child2Data);
        childrenData.add(child3Data);
        //传入数据,设置适配器
        final MyExpandableListViewAdapter adapter = new MyExpandableListViewAdapter(groupData,childrenData);
        list.setAdapter(adapter);
        list.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                Toast.makeText(MainActivity.this, "点击了"+adapter.getGroup(groupPosition), Toast.LENGTH_SHORT).show();
                return false;
            }
        });
        list.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                Toast.makeText(MainActivity.this, "点击了"+adapter.getChild(groupPosition,childPosition), Toast.LENGTH_SHORT).show();
                return true;
            }
        });
    }
}

2.MyExpandableListViewAdapter完整代码

public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
    private List<String> groupData;//组数据
    private List<List<String>> childrenData;//子数据
    private Context context;
    public MyExpandableListViewAdapter(List<String> groupData,List<List<String>> childrenData){
        this.groupData = groupData;
        this.childrenData = childrenData;
    }

    public class GroupViewHolder{
        TextView textView;
    }
    public class ChildViewHolder{
        TextView textView;
    }
    //获取组数
    @Override
    public int getGroupCount() {
        return groupData.size();
    }
    //获取指定组的子元素数
    @Override
    public int getChildrenCount(int groupPosition) {
        return childrenData.get(groupPosition).size();
    }
    //获取指定组
    @Override
    public String getGroup(int groupPosition) {
        return groupData.get(groupPosition);
    }
    //获取指定子元素
    @Override
    public String getChild(int groupPosition, int childPosition) {
        return childrenData.get(groupPosition).get(childPosition);
    }
    //获取组的id
    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }
    //获取子元素的id
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }
    //id是否固定,即数据变化后,数据的id是否改变
    @Override
    public boolean hasStableIds() {
        return false;
    }
    //绑定组布局和组数据
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        context = parent.getContext();
        convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
        GroupViewHolder groupViewHolder = new GroupViewHolder();
        groupViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
        groupViewHolder.textView.setText(groupData.get(groupPosition));
        groupViewHolder.textView.setText(getGroup(groupPosition));
        return convertView;
    }
    //绑定子布局和子数据
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
        ChildViewHolder childViewHolder = new ChildViewHolder();
        childViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
        childViewHolder.textView.setText(childrenData.get(groupPosition).get(childPosition));
        return convertView;
    }
    //子数据是否可选择
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}
原创文章 67 获赞 68 访问量 6万+

猜你喜欢

转载自blog.csdn.net/AlpinistWang/article/details/87691545