RecyclerView实现上拉加载更多的正确姿势

4,024 阅读3分钟

最近项目上的需求需要实现下拉刷新和上拉加载更多的功能,RecyclerView下拉刷新我相信安卓的同学都会做,无非是利用SwipeRefreshLayout,然后给swipeRefreshLayout设置刷新监听,非常简单,所以这里不作赘述。

上拉加载更多,除了必要的逻辑,还需要自己控制loadingView的显示与隐藏,ListView中有一个方法addFooterView()可以轻松实现,在需要显示loadingView的时候设置view的可见性即可。

而RecyclerView中没有相应的方法,因此还需要自己实现。这也是有别于下拉刷新的一个难点。

接下来就一步步实现一个下拉刷新,上拉加载更多的RecyclerView,先来看看最终的效果:

在这里插入图片描述

步入正题,先贴出activity的布局代码

在这里插入图片描述

只需要达到演示效果就行,布局文件不用太复杂

是个RecyclerView就需要RecyclerView.Adapter,所以接下来我们创建个Adapter类

public class MRecyclerViewAdapter extends RecyclerView.Adapter<MRecyclerViewAdapter.MViewHolder> {
  
}

其中MViewHolder是个MRecyclerViewAdapter的内部类

class MViewHolder extends RecyclerView.ViewHolder {
    View mItemView;

    public MViewHolder(@NonNull View itemView) {
        super(itemView);
        this.mItemView = itemView;
    }
}

接下来实现Adpater的三个方法: 在这里插入图片描述

其中:

public static final int TYPE_FOOTER = -1001;
public static final int TYPE_NORMAL = -1000;
private List<RecyclerItem> mDataList = new ArrayList<>();

而RecyclerItem是我们自定义的一个实体类,看看它的内部结构

public class RecyclerItem {
    public int type;        // 布局类型
    public String name;
}

RecyclerItem里面除了有必要的字段之外,还有一个type属性,这个属性就是区分不同布局用的。

看到**onCreateViewHolder()**里的实现,判断viewType类型来生成View,如果viewType = TYPE_FOOTER,则生成我们底部的loadingView,也就是正在加载

而onCreateViewHolder()的viewType是由getItemViewType()这个方法返回的,所以我们还要重写这个方法,返回我们RecyclerItem中的type:

@Override
public int getItemViewType(int position) {
    return mDataList.get(position).type;
}

我们希望,在RecyclerView滚动底部的时候能够自动创建一个loadingView,但是我们mDataList中如果不添加type = TYPE_FOOTER类型的RecyclerItem,onCreateViewHolder()是不会给我们创建这样一个布局的,所以Adapter中还需要提供一个方法,能够帮我们添加一个type = TYPE_FOOTER的RecyclerItem。

在MRecyclerAdapter中创建方法:

public void addLoadItem() {
    if (mDataList.size() > 0 
        && mDataList.get(mDataList.size() - 1).type == TYPE_FOOTER)
        return;

    RecyclerItem footItem = new RecyclerItem();
    footItem.type = TYPE_FOOTER;
    mDataList.add(footItem);
    notifyItemChanged(mDataList.size() - 1);

}

同时,对应的也有removeLoadItem()方法:

public void removeLoadItem() {
    if (mDataList.size() == 0)
        return;
    if (mDataList.get(mDataList.size() - 1).type != TYPE_FOOTER)
        return;

    mDataList.remove(mDataList.size() - 1);
    notifyItemChanged(mDataList.size() - 1);

}

MRecyclerViewAdapter方面的工作准备好了,就剩Activity那边的调用了,给RecyclerView准备Adapter和List数据都是千篇一律的操作,这里不贴他们的代码了,swipeRefreshLayout也是一样。

这里重点关注当RecyclerView滑动到底部时,怎么让loadingView显示出来并进行加载数据的操作。

所以我们要给RecyclerView设置一个滑动的监听,如果当前最后一个ItemView可见了,就显示loadingView。

在这里插入图片描述

简单解释一下,当我们最后一个itemView可见了而且此时并没有在刷新,也没有正在加载更多数据的情况下,就调用adapter的addLoadItem()方法,这个方法会帮我们在recyclerview最后面添加一个loadingView的视图并刷新界面。

有朋友反应加载更多时会报 warning,修改onScrolled()方法的部分代码:

if (totalCount > 0) {
		// 在 recyclerView 滚动时向列表中添加item 并调用 notifyItemInserted() 方法更新时,
		// 系统会给出 warning: 可能会影响RecyclerView滑动时的高度等测量
		// 所以将这次更新 UI 的操作,延迟到下一帧绘制。
		recyclerView.post(() -> {
			mAdapter.addLoadItem();
		})
}

接着加载我们的数据调用pullData(),pullData()获取的数据都是本地生成的list,刷新比较快,所以为了更好地演示效果,这里加了1s的延迟。最终的效果,就是文章开头那样啦。

源码地址在这,有需要的朋友自取~

关于RecyclerView的拖拽操作,我还写了另一篇文章来介绍,有兴趣的朋友可以阅读: 高端操作!实现RecyclerView的上下拖拽

兄dei,如果觉得我写的还不错,麻烦帮个忙呗 :-)

  1. 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#^.^#)
  2. 不用点收藏,诶别点啊,你怎么点了?这多不好意思!
  3. 噢!还有,我维护了一个路由库。。没别的意思,就是提一下,我维护了一个路由库 =.= !!

拜托拜托,谢谢各位同学!