开发者社区> 行者武松> 正文

详解RecyclerView下拉刷新与上拉更多

简介:
+关注继续查看


前言

在原来的文章中我提及了如何使用RecyclerView添加header与footer,今天我们来更深入的扩展一下使用RecyclerView实现常用的下拉刷新与上拉加载更多的功能。当然这些功能的实现也是基于前面的RecyclerView添加header与footer为基础来实现的,不是很了解的可以先看看前面的文章可能能更好的帮助理解。

依赖

为了方法大家的使用我已经把他上传到Jcenter中了,所以大家可以调用下面的代码了直接获取使用:


  1. compile 'com.idisfkj.enchancerecyclerview:mylibrary:1.1.1' 

EnhanceRecyclerView

我将这个扩展的RecyclerView命名为EnhanceRecyclerView,继承RecyclerView。我们知道既然要实现下拉刷新与上拉更多自然先要实现头部与尾部的布局,所以我们先利用前面的知识来为EnhanceRecycleView添加header与footer


  1. public void initView() { 
  2.         View headerView = LayoutInflater.from(getContext()).inflate(R.layout.head_layout, null); 
  3.         View footerView = LayoutInflater.from(getContext()).inflate(R.layout.footer_layout, null); 
  4.         addHeaderView(headerView); 
  5.         addFooterView(footerView); 
  6.     } 

其中的布局文件就不多说了,至于addHeaderView与addFooterView方法可以查看我前面的那篇文章,有详细的介绍

设置监听器

既然要实现下拉刷新与上拉加载,自然少不了对监听器的处理,所以下面来详细介绍下对监听器OnScrollListener与OnTouchListener的处理。

OnScrollListener

为EnhanceRecyclerView添加addOnScrollListener实现其中的onScrollStateChanged与onScrolled方法。

onScrolled

在onScrolled中我们主要做的是获取EnhanceRcyclerView中item的总数量、视图显示中的第一个item在EnhanceRecyclerView中所处的位置与视图显示中最后一个item在EnhanceRecyclerView中所处的位置。

对于item的总数量很好获取直接调用


  1. totalCount = getLayoutManager().getItemCount(); 

由于RecyclerView能实现LinearLayoutManager、GridLayoutManager与StaggeredGridLayoutManager不同的布局,所以另外两个要根据不同的manager来获取,还是看具体代码吧


  1. if (getLayoutManager() instanceof LinearLayoutManager) { 
  2.                     lastItem = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition(); 
  3.                     firstVisible = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition(); 
  4.                 } else { 
  5.                     into = ((StaggeredGridLayoutManager) getLayoutManager()).findLastVisibleItemPositions(into); 
  6.                     firstInto = ((StaggeredGridLayoutManager) getLayoutManager()).findFirstVisibleItemPositions(firstInto); 
  7.                     lastItem = into[0]; 
  8.                     firstVisible = firstInto[0]; 
  9.                 } 

onScrollStateChanged

获取到了那三个关键数据以后,就可以在onScrollStateChanged中实现具体的逻辑,在这个方法中主要实现的是对上拉加载更多的处理


  1. if (lastItem == adapter.getItemCount() + 1 && newState == RecyclerView.SCROLL_STATE_IDLE && !isLoad) { 
  2.                     ViewGroup.LayoutParams params = getFooterView(0).getLayoutParams(); 
  3.                     params.width = RecyclerView.LayoutParams.MATCH_PARENT; 
  4.                     params.height = RecyclerView.LayoutParams.WRAP_CONTENT; 
  5.                     getFooterView(0).setLayoutParams(params); 
  6.                     getFooterView(0).setVisibility(View.VISIBLE); 
  7.                     smoothScrollToPosition(totalCount); 
  8.                     isLoad = true
  9.                     loadMoreListener.onLoadMore(); 
  10.                 } 
  11.                 if (firstVisible == 0) { 
  12.                     isTop = true
  13.                 } else { 
  14.                     isTop = false
  15.                     RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) getHeaderView(0).getLayoutParams(); 
  16.                     params.width = RecyclerView.LayoutParams.MATCH_PARENT; 
  17.                     params.height = RecyclerView.LayoutParams.WRAP_CONTENT; 
  18.                     params.setMargins(0, -getHeaderView(0).getHeight(), 0, 0); 
  19.                     getHeaderView(0).setLayoutParams(params); 
  20.                 } 

简单说明下,核心就是判断lastItem是否处在最后的位置,如果是的话就继续加载更多的操作,这里提供了一个对数据处理的接口所以只要实现loadMoreListener.onLoadMore();即可。

上拉加载更多核心就是这么多,其它的可以查看源码

OnTouchListener

这个监听器主要是对下拉刷新进行处理。我们要分别对其中我们所熟悉的MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE与MotionEvent.ACTION_UP进行处理。ACTION_DOWN就是简单的获取按下的坐标位置,这里就不多说了,下面主要的针对另外的两个进行简单说明。

ACTION_MOVE

这做的逻辑就是对触摸后的处理,根据滑动的距离来动态的改变header的文本与布局视图的显示。


  1. public void touchMove(MotionEvent event) { 
  2.         endY = event.getY(); 
  3.         moveY = endY - startY; 
  4.         //防止item向上滑出 
  5.         if (moveY > 0 && !isRefreshing) { 
  6.             //防止回退文本显示异常 
  7.             scrollToPosition(0); 
  8.  
  9.             if (getHeaderView(0).getVisibility() == GONE) 
  10.                 getHeaderView(0).setVisibility(VISIBLE); 
  11.  
  12.             RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) getHeaderView(0).getLayoutParams(); 
  13.             params.width = RecyclerView.LayoutParams.MATCH_PARENT; 
  14.             params.height = RecyclerView.LayoutParams.WRAP_CONTENT; 
  15.             //使header随moveY的值从顶部渐渐出现 
  16.             if (moveY >= 400) { 
  17.                 moveY = 100 + moveY / 4; 
  18.             } else { 
  19.                 moveY = moveY / 2; 
  20.             } 
  21.             viewHeight = getHeaderView(0).getHeight(); 
  22.             if (viewHeight <= 0) 
  23.                 viewHeight = 130; 
  24.             moveY = moveY - viewHeight; 
  25.             params.setMargins(0, (int) moveY, 0, 0); 
  26.             getHeaderView(0).setLayoutParams(params); 
  27.             if (moveY > 80) { 
  28.                 text.setText(getResources().getString(R.string.release_to_refresh)); 
  29.             } else { 
  30.                 text.setText(getResources().getString(R.string.pull_to_refresh)); 
  31.             } 
  32.         } else { 
  33.             if (getHeaderView(0).getVisibility() != GONE && !isRefreshing) { 
  34.                 getHeaderView(0).setVisibility(GONE); 
  35.             } 
  36.         } 
  37.     } 

至于下拉时与顶部的距离变化是通过设置margin来动态改变的。

ACTION_UP

最后的触摸处理就是在离开屏幕时根据滑动的距离,是否调用加载数据的接口,或者隐藏下拉刷新头部,具体还是看代码吧。


  1. public void touchUp() { 
  2.         if (!isRefreshing && (endY -startY) != 0 ) { 
  3.  
  4.             RecyclerView.LayoutParams params1 = (RecyclerView.LayoutParams) getHeaderView(0).getLayoutParams(); 
  5.             params1.width = RecyclerView.LayoutParams.MATCH_PARENT; 
  6.             params1.height = RecyclerView.LayoutParams.WRAP_CONTENT; 
  7.  
  8.             if (moveY >= 80) { 
  9.                 text.setText(getResources().getString(R.string.refreshing)); 
  10.                 params1.setMargins(0, 0, 0, 0); 
  11.                 isRefreshing = true
  12.                 //刷新数据 
  13.                 pullToRefresh.onRefreshing(); 
  14.             } else { 
  15.                 if (viewHeight <= 0) 
  16.                     viewHeight = 130; 
  17.                 params1.setMargins(0, -viewHeight, 0, 0); 
  18.                 getHeaderView(0).setVisibility(GONE); 
  19.             } 
  20.             getHeaderView(0).setLayoutParams(params1); 
  21.         } 
  22.     } 

代码中重要的地方都有指出相信都能看懂,这样下拉与上拉的逻辑就基本实现了,下面来看接口的设计吧

下拉与上拉接口


  1. public interface PullToRefreshListener { 
  2.         void onRefreshing(); 
  3.     } 
  4.  
  5.     public void setPullToRefreshListener(PullToRefreshListener pullToRefresh) { 
  6.         if (loadMoreListener == null) { 
  7.             initListener(); 
  8.         } 
  9.         this.pullToRefresh = pullToRefresh; 
  10.     } 
  11.  
  12.     public interface LoadMoreListener { 
  13.         void onLoadMore(); 
  14.     } 
  15.  
  16.     public void setLoadMoreListener(LoadMoreListener loadMoreListener) { 
  17.         if (pullToRefresh == null) { 
  18.             initListener(); 
  19.         } 
  20.         this.loadMoreListener = loadMoreListener; 
  21.     } 

在运用是添加接口监听时初始化前面为EnhanceRecyclerView所设置的监听。

状态重置设置

在调用下拉刷新或者上拉加载更多之后,我们为其构造通用方法实现,状态的重置与数据的更新,方便统一调用。


  1. public void setLoadMoreComplete() { 
  2.         RecyclerView.LayoutParams params = (LayoutParams) getFooterView(0).getLayoutParams(); 
  3.         params.width = 0; 
  4.         params.height = 0; 
  5.         getFooterView(0).setLayoutParams(params); 
  6.         getFooterView(0).setVisibility(View.GONE); 
  7.         this.getAdapter().notifyDataSetChanged(); 
  8.         isLoad = false
  9.     } 
  10.  
  11.     public void setRefreshComplete() { 
  12.         RecyclerView.LayoutParams params1 = (RecyclerView.LayoutParams) getHeaderView(0).getLayoutParams(); 
  13.         params1.width = RecyclerView.LayoutParams.MATCH_PARENT; 
  14.         params1.height = RecyclerView.LayoutParams.WRAP_CONTENT; 
  15.         params1.setMargins(0, -getHeaderView(0).getHeight(), 0, 0); 
  16.         getHeaderView(0).setLayoutParams(params1); 
  17.         getHeaderView(0).setVisibility(GONE); 
  18.         this.getAdapter().notifyDataSetChanged(); 
  19.         isRefreshing = false
  20.     } 

所用工作已经完成下面来做个调用示范

使用

xml中引用


  1. <com.idisfkj.mylibrary.EnhanceRecyclerView 
  2.         android:id="@+id/recyclerView" 
  3.         android:layout_width="match_parent" 
  4.         android:layout_height="match_parent"
  5. </com.idisfkj.mylibrary.EnhanceRecyclerView> 

设置监听


  1. mRecyclerView.setPullToRefreshListener(new com.idisfkj.mylibrary.EnhanceRecyclerView.PullToRefreshListener() { 
  2.             @Override 
  3.             public void onRefreshing() { 
  4.                 refreshData(); 
  5.             } 
  6.         }); 
  7.         mRecyclerView.setLoadMoreListener(new EnhanceRecyclerView.LoadMoreListener() { 
  8.             @Override 
  9.             public void onLoadMore() { 
  10.                 loadMoreData(); 
  11.             } 
  12.         }); 

refreshData()与loadMoreData()加载数据的逻辑就不展示了,只是要记住在请求网络数据完之后要在他们中调用相应的mRecyclerView.setRefreshComplete()与 mRecyclerView.setLoadMoreComplete()来重置状态。

至于其他的Adapter、LayoutManager等的设置就不多说了,与原生的RecyclerView是一样的。

总结

其实总的来说难点有两个

添加header与footer。这个前面已经攻克了,而且原理也相对简单

实现触摸与滑动监听逻辑。这个主要是对逻辑的理解,对整个刷新的过程做个整体分析,就能很好的理解上面的代码。对其中视图的动态显示做相应的变化与接口的调用就能很好的处理这些工程。

当然上面的实现可能还有瑕疵,希望指出,我会相应的做修改或者你们修改后可以提交给我,我统一做修改,谢谢!


作者:idisfkj

来源:51CTO

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Android StaggeredGridLayoutManager布局RecyclerView在滚动状态可见范围刷新数据
Android StaggeredGridLayoutManager布局RecyclerView在滚动状态可见范围刷新数据 之所以把StaggeredGridLayoutManager布局的RecyclerView单列出...
2940 0
Android ListView下拉/上拉刷新:设计原理与实现
 《Android ListView下拉/上拉刷新:设计原理与实现》 Android上ListView的第三方开源的下拉刷新框架很多,应用场景很多很普遍,几乎成为现在APP的通用设计典范,甚至谷歌官方都索性在Android SDK层面支持下拉刷新,我之前写了一篇文章《Android SwipeRefreshLayout:谷歌官方SDK包中的下拉刷新》专门介绍过(链接地址:http://blog.csdn.net/zhangphil/article/details/46965377  )。
873 0
Android SwipeRefreshLayout:谷歌官方SDK包中的下拉刷新
 《Android SwipeRefreshLayout:谷歌官方SDK包中的下拉刷新》 下拉刷新在如今移动开发中应用如此广泛和普遍,以至于谷歌干脆在SDK中给予支持。
757 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20002 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
23529 0
SmartRefreshLayout + BaseRecyclerviewAdapterHelper 使用MVP方式实现下拉刷新
关键字:SmartRefreshLayout使用 下拉刷新 上拉加载 BaseRecyclerviewAdapterHelper 前言 下拉刷新和上拉加载是每个APP中最基本的功能,这里将这个功能进行整理。
3795 0
+关注
行者武松
杀人者,打虎武松也。
17112
文章
2569
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载