自定义RecyclerView实现下拉刷新,加载更多

匿名 (未验证) 提交于 2019-12-02 22:56:40

RecyclerView出来的时间已经不短了,现在估计大部分的列表类的需求实现首选肯定是RecyclerView,基本上可以跟ListView说再见了。那么问题来了,一般情况下一个列表页面都会有下拉刷新和加载更多功能,RecyclerView本身并没有下拉刷新和加载更多功能,当然现在已经有很多优秀的开源的支持下拉刷新,加载更多功能的三方RecyclerView,可以直接拿过来用。但是。。。有时候光会用是不够的,还需要知道它们是这么实现的,实现的原理是什么。下面就来介绍一下RecyclerView下拉刷新,加载更多功能的实现套路。

要达到上面的效果首先要考虑的是这个顶部下拉的刷新的view和底部加载更多的view放在什么地方合适,答案就是自定义一个WrapAdapter适配器,通过包装Adapter来提供header和footer。因为RecyclerView的Adapter是支持显示多种不同类型的view的,只需要重写RecyclerView.Adapter的 getItemViewType(int position)方法,根据不同位置返回不同类型即可。可以利用这个特性把第0个位置和最后一个位置预留出来,固定把第0个item存放下拉刷新的view,把最后一个位置存放加载更多的view。 
具体代码如下:

/**  *  实现显示头部和尾部item的adapter,把头部尾部的事情交给这个adapter来做,其他的交给子adapter  */ public class MyWrapAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {      public ArrayList<View> headerViews=new ArrayList<>();     public ArrayList<View> footViews=new ArrayList<>();     public RecyclerView.Adapter adapter;      public MyWrapAdapter(RecyclerView.Adapter adapter, ArrayList<View> headerViews, ArrayList footViews){         this.adapter=adapter;         this.headerViews=headerViews;         this.footViews=footViews;     }      @Override     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {         if(viewType== RecyclerView.INVALID_TYPE){             //头部item             return new RecyclerView.ViewHolder(headerViews.get(0)){};         }else if(viewType== (RecyclerView.INVALID_TYPE-1)){             //尾部item             return new RecyclerView.ViewHolder(footViews.get(0)){};         }         return adapter.onCreateViewHolder(parent,viewType);     }      @Override     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {         if(position>=0&&position<headerViews.size()){             return;         }          if(adapter!=null){             int p=position-headerViews.size();             if(p<adapter.getItemCount()){                 adapter.onBindViewHolder(holder,p);             }         }     }      @Override     public int getItemViewType(int position) {         if(position>=0&&position<headerViews.size()){             //如果是头部则返回一个不可用的标识,表示这是头部item             return RecyclerView.INVALID_TYPE;         }          if(adapter!=null){             int p=position-headerViews.size();             if(p<adapter.getItemCount()){                 return adapter.getItemViewType(p);             }         }          return RecyclerView.INVALID_TYPE-1;//默认返回表示是尾部的item     }      @Override     public int getItemCount() {         return getCount();     }      public int getCount(){         int count=headerViews.size()+footViews.size();         if(adapter!=null){             count+=adapter.getItemCount();         }         return count;     }  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

整体实现下拉刷新的思路是,当把下拉刷新view添加到adapter中的第0个item的时候,高度是0,也就看不见下拉刷新的view。当我们下拉刷新的时候根据滑动的值来设置下拉刷新view的高度,这样下拉刷新的view就慢慢的显示出来了。但是现在的效果是,先看到的是顶部,下拉刷新view是从顶部开始一点一点显示的。这样的效果当然可以,但是我们需要的效果是下拉刷新的view从底部开始一点点显示出来,做如下调整,我们一开始让下拉刷新的view的高度就是自身高,让它距离顶部的距离是负的自身高度,这样正好就看不见了。并且把下拉刷新view的Gravity设置为Gravity.BOTTOM,这样当我们滑动的时候下拉刷新的view就会从底部开始一点一点显示出来。

现在思路是有了,但是实现的时候,需要解决下面几个问题: 
1、下拉刷新只有在滑动到顶部的时候,才会触发下拉刷新,那么RecyclerView如何判断滑动到了顶部? 
研究了几个开源的下拉刷新RecyclerView,发现每个实现的方法都不太一样,踩了几个坑后。决定用 
ViewCompat.canScrollVertically()这个方法来实现,这个方法能够检测出RecyclerView是否滑动到了顶部或底部。 
2、如何获得滑动的值 
这个老套路了,通过重写RecyclerView的onTouchEvent来计算获得。还有一共方式可以获得滑动的值,重写LayoutManager的scrollVerticallyBy方法这个方法的参数值是LayoutManager帮我们计算好的滑动距离,但是用这种方式还得自己包一个LayoutManager,所以不考虑,还是老套路吧。。。

加载更多就比较好实现了,核心是如何判断recyclerview滚动到了底部。 
一种思路给RecyclerView添加addOnScrollListener监听,重写onScrollStateChanged方法,在这里面判断最后一个可见的view是否是最后一个item即可。 
判断的代码如下:

this.addOnScrollListener(new OnScrollListener() {             @Override             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {                 super.onScrolled(recyclerView, dx, dy);             }              @Override             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                 super.onScrollStateChanged(recyclerView, newState);                 if (isRefresh) {                     return;                 }                 if (mState != STATE_NORMAL) {                     return;                 }                 //判断是否最后一item个显示出来                 LayoutManager layoutManager = getLayoutManager();                  //可见的item个数                 int visibleChildCount = layoutManager.getChildCount();                 if (visibleChildCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && !isLoadMore) {                     View lastVisibleView = recyclerView.getChildAt(recyclerView.getChildCount() - 1);                     int lastVisiblePosition = recyclerView.getChildLayoutPosition(lastVisibleView);                     if (lastVisiblePosition >= layoutManager.getItemCount() - 1) {                         footerView.setVisibility(VISIBLE);                         isLoadMore = true;                         if (myRecyclerViewListener != null) {                             myRecyclerViewListener.onLoadMore();                         }                     } else {                         footerView.setVisibility(GONE);                     }                 }              }         });

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!