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

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

内容简介:纵观多数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


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

查看所有标签

猜你喜欢:

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

人人都在说谎

人人都在说谎

赛思·斯蒂芬斯--达维多维茨 / 胡晓姣、张晨、左润男 / 中信出版集团 / 2018-11 / 58

有多少人买了书真正看完了? 你朋友的酒量有他说的那么大吗? 父母是否暗自喜欢男孩儿多于女孩儿? 电影里暴力镜头增多会导致犯罪率升高吗? 种族歧视在现如今的美国还严重吗? 特朗普的胜利有征兆吗,什么促成他赢得了大选? …… 你知道问题的答案吗,直觉会怎样告诉你? 作者赛思·斯蒂芬斯--达维多维茨是一位前谷歌数据科学家和专栏作家。他的研究发现,世界充满了......一起来看看 《人人都在说谎》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具