Android RecyclerView 使用详解

数字寻汐人
• 阅读 9304

一、前言

<font face = 黑体>可以这样说,RecyclerView 的问世,替代了 ListView 和 GridView。RecyclerView 异常的灵活可自定义并可重复利用的 Item高度的解耦,并且通过设置不同的 LayoutManager、ItemDecoration 和 ItemAnimator 可以实现令人瞠目的效果。

<font face = 黑体>ItemDecoration 的相关讲解大家可以看 这篇文章

<font face = 黑体>源码已经上传至 github,地址在文末中已经给出,可以先下载到本地运行起来,因为具体事例讲解的时候并没有把全部的代码贴出来。

二、与 RecyclerView 配合的类

  • <font face = 黑体>LayoutManager:布局管理器,管理 RecyclerView 的展示样式,系统提供了列表、网格和瀑布流三种形式的布局管理器。
  • <font face = 黑体>Adapter:适配器,用来处理视图与数据之间的关系。
  • <font face = 黑体>ViewHolder:用来容纳 View 视图。

三、案例介绍

<font face = 黑体>我们要实现的最终效果如下所示:

<font face = 黑体>可以看到界面中有四个按钮,分别是添加数据、切换布局,插入一条数据和删除一条数据。当点击添加数据的时候,RecyclerView 就会以线性布局的方式展示出数据,当点击切换布局的时候,RecyclerView 会先切换成网格布局,然后再切换成瀑布流布局,当点击插入和删除数据的时候,RecyclerView 会根据默认的动画来执行,并且动态更新 ItemView 的 position。

Android RecyclerView 使用详解

四、实现线性布局列表

4.1、引入 RecyclerView

<font face = 黑体>要使用 RecyclerView,首先我们需要在 app 的 build.gradle 中引入 RecyclerView 库。

implementation 'androidx.recyclerview:recyclerview:1.1.0'

4.2、创建适配器

<font face = 黑体>想要把数据展示在列表视图中,就需要通过适配器,所以我们这里创建 MyRecyclerViewAdapter 继承自 RecyclerView 的 Adapter,并实现其中的三个方法,这里我们以内部类的方式来实现 MyViewHolder,同样是继承自 RecyclerView 的 ViewHolder,具体代码如下所示:(源码文末给出)

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {

    private Context mContext;
    private List<String> dataSource;

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public MyRecyclerViewAdapter(Context mContext) {
        this.mContext = mContext;
        this.dataSource = new ArrayList<>();
    }

    public void setDataSource(List<String> dataSource) {
        this.dataSource = dataSource;
        notifyDataSetChanged();
    }

    /**
     * 创建并且返回 ViewHolder
     *
     * @param parent
     * @param viewType
     * @return
     */
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false));
    }

    /**
     * 通过 ViewHolder 来绑定数据
     *
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
        holder.mIv.setImageResource(getIcon(position));
        holder.mTv.setText(dataSource.get(position));
    }

    /**
     * 返回数据数量
     *
     * @return
     */
    @Override
    public int getItemCount() {
        return dataSource.size();
    }

    // 获取图片
    private int getIcon(int position) {
        switch (position % 5) {
            case 0:
                return R.drawable.a;
            case 1:
                return R.drawable.b;
            case 2:
                return R.drawable.c;
            case 3:
                return R.drawable.d;
            case 4:
                return R.drawable.e;
        }
        return 0;
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        private ImageView mIv;
        private TextView mTv;
        private View mItemView;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);

            mIv = itemView.findViewById(R.id.iv);
            mTv = itemView.findViewById(R.id.tv);
            mItemView = itemView;
        }
    }
}

4.3、在 MainActivity 使用适配器

<font face = 黑体>在 MainActivity 中实例化 Adapter,掉用 RecyclerView .setAdapter() 方法来显示数据,当点击添加数据的时候,就将模拟数据通过 setDataSource() 方法设置过去,在该方法中我们写了 notifyDataSetChanged() 方法,所以,数据设置完后会通知界面展示。

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private MyRecyclerViewAdapter mAdapter;

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

        mRecyclerView = findViewById(R.id.recycler_view);

        // 线性布局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);

        mRecyclerView.setLayoutManager(linearLayoutManager);
        mAdapter = new MyRecyclerViewAdapter(this, mRecyclerView);
        mRecyclerView.setAdapter(mAdapter);
    }

    /**
     * 添加数据
     * @param v
     */
    public void onAddDataClick(View v) {
        List<String> data = new ArrayList<>();

        for (int i = 0; i < 20; i++) {
            String s = "第" + i + "条数据";
            data.add(s);
        }

        mAdapter.setDataSource(data);
    }
}

4.4、效果展示

<font face = 黑体>具体效果如下所示:

Android RecyclerView 使用详解

五、切换布局

5.1、网格布局

<font face = 黑体>RecyclerView 的布局是通过 LayoutManager 来设置的,使用网格布局只需要实例化 GridLayoutManager,并调用RecyclerView.setLayoutManager() 方法就可以了, 以下代码中的 2 的意思是每一行都有两列数据。

public void onChangeLayoutClick(View v) {
    // 网格布局
    if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        mRecyclerView.setLayoutManager(gridLayoutManager);
    }
}

5.2、瀑布流布局

<font face = 黑体>所谓瀑布流布局就是指 ItemView 的宽度相同,但是高度不同。同样,我们只需要实例化 StaggeredGridLayoutManager 并设置到 RecyclerView 里面去就可以了。

public void onChangeLayoutClick(View v) {
    // 网格布局
    if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        mRecyclerView.setLayoutManager(gridLayoutManager);
    }
    // 瀑布流布局
    else if (mRecyclerView.getLayoutManager().getClass() == GridLayoutManager.class) {
        StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
    }
    // 线性布局
    else if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(linearLayoutManager);
    }
}

<font face = 黑体>这里为了模拟不同高度,所以我们在Adapter中写了一个返回随机高度的方法,并且在 onBindViewHolder() 方法中判断当前使用的布局是不是瀑布流布局,如果是的话,就使用随机高度。

private int getRandomHeight() {
    return (int) (Math.random() * 1000);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
    holder.mIv.setImageResource(getIcon(position));
    holder.mTv.setText(dataSource.get(position));

    /**
     * 只在瀑布流布局中使用随机高度
     */
    if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
        holder.mTv.setLayoutParams(params);
    } else {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        holder.mTv.setLayoutParams(params);
    }
}

5.3、效果展示

<font face = 黑体>具体效果如下所示:

Android RecyclerView 使用详解

六、插入数据与删除数据

<font face = 黑体>我们在 Adapter 中定义如下添加数据和删除数据的方法,然后在 MainActivity 中调用就可以了,RecyclerView 的添加数据和删除数据会使用默认的动画,当然你也可以自定义动画。这里需要特别注意的是,当添加删除完数据后,一定要调用 notifyItemRangeChanged() 这个方法,去刷新 ItemView,这个方法的第一个参数的意思是刷新的起点,第二个参数是刷新的数量。

/**
 * 添加一条数据
 * @param position
 */
public void addData(int position) {
    addDataPosition = position;
    dataSource.add(position, "插入的数据");
    notifyItemInserted(position);

    notifyItemRangeChanged(position, dataSource.size() - position);
}

/**
 * 删除一条数据
 * @param position
 */
public void removeData(int position) {
    addDataPosition = -1;
    dataSource.remove(position);
    notifyItemRemoved(position);

    notifyItemRangeChanged(position, dataSource.size() - position);
}

6.1、效果展示

<font face = 黑体>具体效果如下所示:

Android RecyclerView 使用详解

七、小结

<font face = 黑体>这篇文章基本上已经将 RecyclerView 的基本使用都讲完了,RecyclerView 的作用是在有限的显示空间里面去展示大量的数据,RecyclerView 本身只负责回收和复用 View,它需要与其他类的配合来达到展示数据的效果,比如说通过配合 Adapter 适配器来把视图与数据连接起来,通过配合 LayoutManager 来确定数据的展示形式(线性、网格、瀑布流),另外 RecyclerView 并没有提供 Item的点击事件响应方法,这个需要我们自己去实现,源码中我已经实现了这个方法,因为比较简单所以上述就没有讲了。

八、源码

<font face = 黑体>源码已经上传至 github,大家直接下载就可以了。

点赞
收藏
评论区
推荐文章
字节小站 字节小站
4年前
干货|详解位图算法在Android RecyclerView中的应用
1.前言1.1关于算法金庸武侠小说中的主人公在成为绝世高手之前,都会学习一门玄门内功。郭靖有了全真派的内功才能修炼九阴真经、虚竹得到了无崖子的毕生功力后,武学造诣日渐精进、张无忌苦练五年九阳神功,日后才能融合乾坤大挪移。对于程序员,算法就是小说中的内功,编程语言就是不同门派的武功。张无忌因为有九阳神功加持仅用一天就学会了阳顶天几十年都学不成的乾
红橙Darren 红橙Darren
4年前
RecyclerView更全解析之 - 为它优雅的添加头部和底部
1.概述上一期的,解决了几个坑。那么这一期我们来动态为RecyclerView去加载头部和底部,为上一期的RecyclerView列表数据添加广告轮播图,至于广告轮播大家可以看一下这一期,这里我就不多讲了,直接拿过来用。    视频讲解:相关文章:                        
御弟哥哥 御弟哥哥
4年前
Android RecyclerView如何获取滑动距离
获取RecyclerView滑动的距离。本文演示如何获取RecyclerView的滑动距离。要实现这个功能,需要给RecyclerView添加滑动时监听RecyclerView.OnScrollListener。recyclerView.addOnScrollListener(newRecyclerView.OnScrollListene
红橙Darren 红橙Darren
4年前
RecyclerView更全解析之 - 基本使用和分割线解析
1.概述昨天跟自己群里的人唠嗑的时候发现还有人在用Eclipse,我相信可能还是有很多人在用ListView,这里介绍一个已经出来的n年了的控件RecyclerView,实现ListView,GridView,瀑布流的效果。还可以轻松的实现一些复杂的功能,如QQ的拖动排序,侧滑删除等等。相关文章:              
希望的天 希望的天
4年前
RecyclerView基础用法
是一款非常强大的widget,它可以帮助您灵活地显示列表数据。当我开始学习RecyclerView的时候,我发现对于复杂的列表界面有很多资源可以参考,但是对于简单的列表展现就鲜有可参考的资源了。虽然RecyclerView的组成结构乍一看有些复杂,但是深入理解以后您会发现它其实非常简单明了。本文会通过创建一个简单的RecyclerView实现一
Stella981 Stella981
3年前
Android RecyclerView使用GridLayoutManager间距设置
使用RecyclerView设置间距,需要重写RecyclerView.ItemDecoration这个类。有如下的效果图需要实现,间距只有中间的格子和底部的格式之间有。Paste\_Image.png实现方法很简单,因为这个效果是每一行有3个格子,只要每行的第一个格式左边间距为0即可以。其他都设置左边距和底部距离。代码如下:publ
Stella981 Stella981
3年前
Android 解决NestedScrollView 嵌套 RecyclerView出现的卡顿,上拉刷新无效
解决卡顿的方法最简单的就是设置RecyclerView的android:nestedScrollingEnabled"false",放弃自己的滑动,交给外部的NestedScrollView处理,就没有出现卡顿的现象了至于RecyclerView的上拉刷新,可以监听NestedScrollView的滑动监听,具体代码如下:mNestedSc
Stella981 Stella981
3年前
Android Recyclerview隐藏item的所在区域显示大空白问题的解决方案
最近搞了下Recyclerview,做了增加、删除item的功能。item上方有卡签!(https://oscimg.oschina.net/oscnet/0145e835209331d61e7a761ab97fdce5bac.png) 插个图片看下效果,点击底下的添加上去,同时,底下的item消失,这个用notifyItemInserted和n
Stella981 Stella981
3年前
Recyclerview的多种条目
packagecomp.example.practise9.adapter;importandroid.content.Context;importandroid.support.v7.widget.RecyclerView;importandroid.view.LayoutInflater;importandroid.view.View
Stella981 Stella981
3年前
ListView+RecyclerView缓存类的封装
publicclassBaseViewHolerextendsRecyclerView.ViewHolder{privateContextcontext;//行布局的viewprivateViewmView;//用来装载id的集合用法和map类似
Stella981 Stella981
3年前
Android ImageView 的scaletype属性详细介绍
android:scaleType是控制图片如何resized/moved来匹对ImageView的size,我们可以这样啦使用1.<ImageViewandroid:id"@id/img\_weibo\_img"2.android:layout\_width"fill\_parent"3.and
数字寻汐人
数字寻汐人
Lv1
不要在深夜里做任何决定,睡一觉吧,明天醒来再说。
文章
4
粉丝
0
获赞
0