Android中RecyclerView的使用与解析

本文详细解析了Android中RecyclerView的原理与使用方法,包括其相较于ListView的优势、基本API使用、Adapter的工作机制及点击事件设置等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android中RecyclerView的使用与解析

用惯了ListView的我最近觉得RecyclerView是越来越好用了,不仅自己不再需要用ViewHolder做种种优化,布局上也更加自由了。本文着重对于RecyclerView的基本原理做一些解析(特别是Adapter的作用和调用过程),并且说一下基本的使用方法。

一、RecyclerView是什么?
中文翻译过来就是循环使用的View,官方把它作为ListView的升级版。为什么说是升级版呢?因为之前我们为了达到“列表”的效果,有很多控件要学习使用,普通点的ListView,支持多行多列的GridView等等。现在只要一个RecyclerView,你就可以实现这各种各样的效果。
总的来说它有两个优点:
1、封装了对ViewHolder的复用,让你面向ViewHolder去编写Adapter,不再需要自己额外的优化了。
2、把列表显示控件进行了高度的解耦,把不同需求的功能进行了模块化的划分,给你提供了非常灵活的编写方式。把显示方式用LayoutManager来控制,间隔用ItemDecoration来控制,增删动画用ItemAnimator来控制,想要改变只需要替换相应的功能模块,是不是非常灵活!

二、RecyclerView的基本API
用惯了ListView,我们想必也不会陌生RecyclerView,下面来看看使用的代码:

recyclerView = (RecyclerView) findViewById(R.id.recyclerView);  
LinearLayoutManager layoutManager = new LinearLayoutManager(this );  
//设置布局管理器  
recyclerView.setLayoutManager(layoutManager);  
//设置Adapter  
recyclerView.setAdapter( recycleAdapter);  
 //设置分隔线  
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));  
//设置增加或删除条目的动画  
recyclerView.setItemAnimator( new DefaultItemAnimator()); 

这里基本上把所有可以自定义的部分都试了一遍,看惯了ListView的肯定会觉得这个RecyclerView怎么这么麻烦。但我已经说了这上面把所有的自定义部分都尝试了,如果只是简单的显示,很简单的代码就可以完成。

三、RecyclerView中的Adapter使用与解析(重要)
我们来看一个最简单的显示例子:
先给大家看一下效果,和传统的ListView没有什么区别:
最简单的效果
界面很简单,放一个RecyclerView的Layout即可

<?xml version="1.0" encoding="utf-8"?>
<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="com.example.lee.recyclerviewtest.MainActivity">

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

    </android.support.v7.widget.RecyclerView>
</RelativeLayout>

然后是我们很熟悉的列表项的布局(这个和ListView一样)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <TextView
        android:id="@+id/id_main_text_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="测试"
        android:textSize="20sp"/>

</LinearLayout>

之后是最重要的Adapter,注意观察之中的特殊之处

package com.example.lee.recyclerviewtest;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;
import java.util.zip.Inflater;

/**
 * Created by Lee on 2016/9/28.
 */
public class MainListAdapter extends RecyclerView.Adapter<MainListAdapter.MainListViewHolder>{
    private List<String> mStringList;
    private LayoutInflater mLayoutInflater;

    public MainListAdapter(Context context, List<String> strings){
        mLayoutInflater = LayoutInflater.from(context);
        mStringList = strings;
    }

    @Override
    public MainListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = mLayoutInflater.inflate(R.layout.item_layout_main_list, parent, false);
        return new MainListViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(MainListViewHolder holder, int position) {
        holder.mainTextView.setText(mStringList.get(position));
    }

    @Override
    public int getItemCount() {
        return mStringList.size();
    }

    public class MainListViewHolder extends RecyclerView.ViewHolder{
        public TextView mainTextView;

        public MainListViewHolder(View itemView){
            super(itemView);
            mainTextView = (TextView) itemView.findViewById(R.id.id_main_text_view);
        }
    }
}


第一次从ListView转过来的时候对于这个Adapter肯定非常熟悉了,但RecyclerView中的Adapter又和传统的ListView中有些区别,我们来看一下传统的ListView的Adapter,从中找出区别能让你更快速理解这个新的Adapter:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ListViewAdapter.ViewHolder holder = null;
        if(convertView == null){
            holder = new ListViewAdapter.ViewHolder();
            convertView = mInflater.inflate(R.layout.item_layout_main_list, null);
            holder.mainTextView = (TextView) convertView.findViewById(R.id.id_main_text_view);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.mainTextView.setText(mStringList.get(position));
        return convertView;
    }

    class ViewHolder{
        TextView mainTextView;
    }

这是传统Adapter中最重要的一个方法(我附加了ViewHolder,以便对比),getView是ListView中用于得到每个项View的接口,最终在obtainView函数中调用。我们来先简单了解一下ListView中Adapter接口的调用过程:
1、调用getView之前,ListView会从缓存中尝试取出缓存的View(当你的某项在屏幕中不可见,它就会进入缓存之中),之后把它作为第二个参数传入getView函数中(如果没有就是null),所以我们在getView中做了判断;
2、如果没有缓存(第一次加载时),我们就从resourceId中创建View,之后进入第二个优化,ViewHolder,为了防止每次都调用findViewById,我们把view中的对应控件用holder存下来并且和view对应起来(Tag);
3、得到了Holder,这个时候无论是新创建的View还是得到的缓存,我们都要对它的数据进行设置,这里我们成为绑定数据,把数据绑定到对应的View上,也就完成了整个getView的过程。

那么我们来看看RecyclerView的Adapter怎么分解这个过程的:
首先,RecyclerView中操作单位不再是View了,而直接是ViewHolder,有两点区别:
1、直接省去了每次都要find的过程,直接用标准的ViewHolder来操作,你创建ViewHolder时就已经把控件找到并且得到了引用,这个过程被放在了构造函数中。
2、缓存的数据不再是View,直接用ViewHolder来进行缓存,如果没有缓存,创建的对象也是ViewHolder(你可以发现onCreateViewHolder的返回值类是ViewHolder)
其实你理解了ListView的操作,你直接通过函数名就可以理解RecyclerView的Adapter接口调用过程:
1、获取ViewHolder:首先RecyclerView尝试获取ViewHolder的缓存,如果没有缓存就调用onCreateViewHolder函数来创建,这其中完成从资源生成View的过程并且根据View创建对应的ViewHodler(构造函数完成find操作):

@Override
    public MainListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = mLayoutInflater.inflate(R.layout.item_layout_main_list, parent, false); //生成View
        return new MainListViewHolder(itemView); //返回的是ViewHolder对象
    }

在ViewHolder的构造函数中,我们可以看到对应的itemView,而之前setTag的绑定工作就不需要我们去完成了,因为itemView本身就是Holder的一个变量,直接就一一对应了,我们只需要在之后调用相应的findViewById来获取相应控件的引用即可

public MainListViewHolder(View itemView){
    super(itemView); //父类完成绑定
    mainTextView = (TextView) itemView.findViewById(R.id.id_main_text_view);
    //获取引用,方便绑定数据(避免多次嗲用findViewById)
}

2、绑定数据:调用onBindViewHolder,绑定数据,把相应的数据绑定到ViewHolder上,即通过相应的控件引用设定相应数据(这样才能正确显示)。

@Override
    public void onBindViewHolder(MainListViewHolder holder, int position) {
        holder.mainTextView.setText(mStringList.get(position)); //绑定数据
    }

我们可以看出onCreateViewHolder只有在第一次创建View的时候才调用,而onBindViewHolder是每次绑定数据都需要的。

通过这个分析我们可以发现,RecyclerView中的Adapter过程和ListView的Adapter过程是密切相关的,官方只是把我们之前需要自己去做的优化标准化了,并且省略了自己去判断的过程,用RecyclerView只需要去完善相应的接口即可。

在了解了这么一个详细的调用过程之后,我们来看一下Activity中的简单代码:

package com.example.lee.recyclerviewtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RecyclerView mMainRecyclerView;
    private List<String> mNames = new ArrayList<String>();

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

        initViews();
    }

    private void initViews(){
        mNames.add("1");
        mNames.add("2");
        mNames.add("3");
        mNames.add("4");
        mNames.add("5");
        mNames.add("6");
        mNames.add("7");
        mNames.add("8");
        mNames.add("9");
        mNames.add("10");
        mMainRecyclerView = (RecyclerView) findViewById(R.id.id_rev_main);
        mMainRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mMainRecyclerView.setAdapter(new MainListAdapter(this, mNames));
    }
}

四、RecyclerView中Item点击事件设置
ListView可以通过setOnItemClickListener来处理item的点击事件,但RecyclerView并没有提供这个接口,那么在RecyclerView中怎么来实现呢?我们可以定义一个接口,并且注入到Adapter中,在绑定数据的时候,在View的点击事件中回掉这个函数即可。
具体做法如下所示:
1、定义接口(可以定义在Adapter中)

public static interface OnItemClickListener{
    public void onClick(View view,int position);
}

2、在Adapter中加入Listener变量

private OnItemClickListener mOnItemClickListener;

public void setOnItemClickListener(OnItemClickListener listener){
    mOnItemClickListener = listener;
}

3、在绑定数据中增加回调过程

    @Override
    public void onBindViewHolder(MainListViewHolder holder, final int position) {
        holder.mainTextView.setText(mStringList.get(position)); //绑定数据
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mOnItemClickListener != null){
                    mOnItemClickListener.onClick(v, position); //回调函数
                }
            }
        });
    }

从回调的这个语句中的holder.itemView我们也可以看出我们之前得出的结论,官方的ViewHolder是具有itemView的引用的(通过这个方式让Holder和itemView一一对应起来)

如果是长按呢?长按同理可得,只需要在接口中加上一个回调即可,如下所示:
1、接口中加入新的函数

public void onLongClick(View view,int position);

2、在绑定数据时多加一个语句

holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        if(mOnItemClickListener!= null){
            mOnItemClickListener.onLongClick(v, position);
        }
        return false;
    }
});

当然你如果只想实现其中一个或者分别用两个也是完全可以的。

五、RecyclerView中的LayoutManager
这是RecyclerView的另一个特点,也是非常赞的特性!刚在的代码中这一句用惯了ListView肯定没有见过:

mMainRecyclerView.setLayoutManager(new LinearLayoutManager(this,        LinearLayoutManager.VERTICAL, false));

这是RecyclerView模块化解耦的一个巨大优势,你可以通过LayoutManager来轻松的实现显示方式的改变,在RecyclerView进行布局的过程中会调用LayoutManager的onLayoutChildren来进行布局,从而让自定义布局变得更加简单和轻松。
作为初学者,自己实现LayoutManager确实有些难了,但系统已经实现了三个常用的布局方式,通过三个实现类:
1、LinearLayoutManager 线性布局管理器,支持水平和垂直两个方向。
2、GridLayoutManager 网格布局管理器
3、StaggeredGridLayoutManager 瀑布式网格布局管理器
我们上面用的就是简单的LinearLayoutManager,我们来看一看网格式,只需要在设置LayoutManager时做一点改动即可,将上面那个语句改为:

mMainRecyclerView.setLayoutManager(new GridLayoutManager(this,4));

我们来看看效果:
20项的效果
是不是觉得非常赞!只需要简单的修改就可以得到这个效果,让我们再来看看瀑布式的效果如何,我又增加了一些项,调整了一下大小,让我们来看一看:
(这里如果是HORIZONTAL就表示有4行,如果是VERTICAL那么就是4列)

mMainRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL));

这里写图片描述
轻松实现瀑布效果,只需要不同的LayoutManager即可。

讲了这么多,我们已经掌握了RecyclerView的基本原理和用法,理解了这个Adapter的调用过程和原理,算是已经入门了。但RecyclerView提供的不仅仅是我介绍的这些,它还提供了分隔线和删除动画的效果,这里我觉得单纯的使用也没有必要写了,我着重讲解得是对于Adapter函数的调用过程和解耦的理解,毕竟理解了过程才能更好地使用它。如果是单纯的使用,我推荐一篇博文,上面对于使用讲解的比较详细大家可以去看一下:
http://blog.youkuaiyun.com/lmj623565791/article/details/45059587

希望我的解析能够让大家更好地理解RecyclerView的原理和使用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值