手把手教你实现RecyclerView的下拉刷新和上拉加载更多

栏目: IOS · Android · 发布时间: 5年前

内容简介:纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做。基于RecyclerView简单封装了这两个操作,下拉刷新支持LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager;上拉加载更多只支持前两者。

纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做。

基于RecyclerView简单封装了这两个操作,下拉刷新支持LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager;上拉加载更多只支持前两者。

话不多说先上效果图 数据来自 干货集中营

手把手教你实现RecyclerView的下拉刷新和上拉加载更多 (下拉刷新)

手把手教你实现RecyclerView的下拉刷新和上拉加载更多 (上拉加载更多 – LinearLayoutManager)

手把手教你实现RecyclerView的下拉刷新和上拉加载更多 (上拉加载更多 – GridLayoutManager)

(一) 使用方式,很简单 如下:

  • 1. 下拉刷新 3步走

1.1 布局文件

// 用SwipeRefreshLayout包裹RecyclerView
<android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/gank_swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

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

1.2 给 SwipeRefreshLayout 添加监听 增加触发刷新时的操作(比如重新请求数据)

SwipeRefreshLayout swipeRefreshLayout = findViewById();
swipeRefreshLayout.setOnRefreshListener(new swipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // do something, such as re-request from server or other
            }
        });

1.3 刷新操作(重新请求数据)完成后要回调来停止隐藏刷新动画(中上方圆形悬浮进度条旋转动画)

swipeRefreshLayout.setRefreshing(false);

至此下拉刷新完成

  • 2.上拉加载3步走

2.1 初始化 AdapterWrapperSwipeToLoadHelper

// adapter是你自己为RecyclerView写的Adapter
RecyclerView.Adapter adapter = new YourOwnAdapter();
AdapterWrapper adapterWrapper = new AdapterWrapper(adapter);
RecyclerView recyclerView = findViewById();
// 将RecyclerView和刚创建的adapterWrapper传入
SwipeToLoadHelper helper = new SwipeToLoadHelper(recyclerView, adapterWrapper);

2.2 设置加载动作触发后的监听

helper.setLoadMoreListener(new SwipeToLoadHelper.LoadMoreListener() {
            @Override
            public void onLoad() {
            // do something, such as request more data from server or other.
            }
        })

2.3 加载更多内容完成后要回调方法停止动画

helper.setLoadMoreFinish()

至此上拉加载完成 (注意更新数据时要调用 AdapterWrapper.notifyDataSetChanged )

(二) 简明扼要的实现思路(上拉加载操作)

RecyclerViewitemView 的显示情况分为四种:

  1. 第1个可见的(部分显示或者完全显示都算可见)
  2. 第1个可见的且是完整的(完全显示算作完整的)
  3. 最后1个可见的
  4. 最后1个可见的且是完整的
  • 1. 回弹效果

    即手指抬起滑动停止, 上拉加载更多 部分显示时,将 上拉加载更多 滚动到不显示,使上面挨着它的 itemView 为最后1个可见且是最后1个完整可见。监听 RecyclerView 的滚动,当 RecyclerView 处于 SCROLL_STATE_IDLE 状态时,获取最后1个完整可见的 itemView :如果是倒数第2个 item 则计算该 item 的下边距到 RecyclerView 底部的距离 deltaY ,然后将 RecyclerView 向下滚动 deltaY ;如果是 上拉加载更多 则触发加载操作;其他情况不用处理。

// 关键代码 rv : recyclerView
int lcp= layoutManager.findLastCompletelyVisibleItemPosition();
if (lcp == layoutManager.getItemCount() - 2) {
    // 倒数第2项
    int fcp= layoutManager.findFirstCompletelyVisibleItemPosition();
    View child = layoutManager.findViewByPosition(lcp);
    int deltaY = rv.getBottom() - rv.getPaddingBottom() - 
                    child.getBottom();
    // fcp为0时说明列表滚动到了顶部, 不再滚动
    if (deltaY > 0 && fcp!= 0) {
          rv.smoothScrollBy(0, -deltaY);
    }
} else if (lcp== layoutManager.getItemCount() - 1) {
    // 最后一项完全显示, 触发操作, 执行加载更多操作
    if (listener!= null) {
        listener.onLoad();
    }
}
  • 2. 添加底部 加载更多 itemView

2.1 AdapterWrapper 重写了 getItemCount 方法,保证得到 itemView 的数量包括 加载更多 。当是 LinearLayoutManager 类型时直接加1;当是 GridLayoutManager 类型时,如果需要则先将列表最后一行填满,再加1。比如:列表每行有3个 itemView ,最后一行只有1个,这时就需要先加2,再加1,来保证 加载更多 占据完整的一行。

// 关键代码 其中的adapter为构造函数中传入的原生RecyclerView.Adapter
if (adapterType == ADAPTER_TYPE_LINEAR) {
    // 线性布局
    return adapter.getItemCount() + 1;
} else {
    // 网格布局 spanCount为每行itemView的个数
    int remain = adapter.getItemCount() % spanCount; // 余数
    if (remain == 0) {
        return adapter.getItemCount() + 1;
    }
    // 余数不为0时,先凑满再加1
    return adapter.getItemCount() + 1 + (spanCount - remain);
}

2.2 AdapterWrapper 重写了 getItemViewType 方法,当是最后一个位置时返回 ITEM_TYPE_LOAD

// 关键代码
public int getItemViewType(int position) {
    // 位置是最后一个时, wrapper进行拦截
    if (osition == getItemCount() - 1) {
        return ITEM_TYPE_LOAD;// 要避免和原生adapter返回值可能重复
    }
    // 其他情况交给原生adapter处理
    return adapter.getItemViewType(position);
}

2.3 AdapterWrapper 重写了 onCreateViewHolder 方法,当类型为 ITEM_TYPE_LOAD 时返回 加载更多ViewHolder ,其他情况交给原生的 adapter 处理。

// 关键代码
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == ITEM_TYPE_LOAD) {
        return new LoadMoreHolder();
    } else {
        return adapter.onCreateViewHolder(parent, viewType);
    }
}

2.4 AdapterWrapper 重写了 onBindViewHolder ,这里有三种可能的情况:1. 正常的数据项 itemView ,交给 adapter 处理;2. GridView 的空白 itemView ,隐藏处理;3. 底部的 加载更多 ,目前不需要做什么处理。

// 关键代码
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (position == getItemCount() - 1) {
    } else if (position < adapter.getItemCount()){
        adapter.onBindViewHolder(holder, position);
    } else {
        holder.itemView.setVisibility(View.INVISIBLE);
    }
 }

(三) 额外的两个说明

  • SwipeFreshLayout 有个 setEnable(boolean) 方法,设置为 false 的时候就下拉刷新功能就没有了,等同于普通的 RecyclerView
  • 同样 SwipeToLoadHelper 有个 setSwipeToLoadEnabled(boolean) 方法,设置为 false 的时候上拉加载功能就没有了, 等同于普通的 RecyclerView

如有问题,欢迎指正~

项目仓库地址 ,如有需要请自取~

——————— 本文来自 是薛定谔啊 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u012402124/article/details/78210639?utm_source=copy


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

We Are the Nerds

We Are the Nerds

Christine Lagorio-Chafkin / Hachette Books / 2018-10-2 / USD 18.30

Reddit hails itself as "the front page of the Internet." It's the third most-visited website in the United States--and yet, millions of Americans have no idea what it is. We Are the Nerds is an eng......一起来看看 《We Are the Nerds》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试