步骤:

  1. 首先直接定义一个XRecyclerView继承RecyclerView,重写他的三个构造方法。
  2. init(Context mContext)方法用来初始化底部加载的view
  3. 回到XRecyclerView,实现init
  4. 判断是否滑动到底部,并且进行加载
  5. 自定义一个adapter来把底部布局加进去。
  6. 重写Adapter,通过状态判断是否显示“正在加载”
  7. 定义一个mDataObserver

    1. 首先直接定义一个XRecyclerView继承RecyclerView,重写他的三个构造方法。

public XRecylcerView(Context context) {
this(context, null);
}
public XRecylcerView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
}
public XRecylcerView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
init(context);
}

    2. init(Context mContext)方法用来初始化底部加载的view

      先自定义一个底部布局LoadingMoreFooter继承Linearlayout,里面是一个居中显示的ProgressBar和一个TextView,添加一个方法setState(int state),来判定当前刷新的状态

public void setState(int state) {
switch (state) {
// 刷新中
      case STATE_LAODING:
         progressCon.setVisibility(View.VISIBLE);
         mText.setText("正在刷新");
         this.setVisibility(View.VISIBLE);
  break;
      // 刷新完成
      case STATE_COMPLETE:
         mText.setText("刷新完成");
         this.setVisibility(View.GONE);
        break;
      // 没有更多数据
      case STATE_NOMORE:
          mText.setText("没有更多数据啦");
progressCon.setVisibility(View.GONE);
this.setVisibility(View.VISIBLE);
break;
    }
}

    3. 回到XRecyclerView,实现init

  private void init(Context context) {
mContext = context;
// loadingMoreEnabled为下拉的开关
if (loadingMoreEnabled) {
LoadingMoreFooter footerView = new LoadingMoreFooter(mContext);
addFootView(footerView);
mFootViews.get(0).setVisibility(GONE);
}
}

      RecyclerView的上拉加载,原理很简单,无非就是当滑动到底部的时候,如果有数据,并且允许加载,就请求数据添加到adapter,而RecyclerView需要做的就是当加载的时候,在底部显示正在加载来提醒用户。  

    4.判断是否滑动到底部,并且进行加载

 /**
* 监听滑动,来定位当前滑动到哪个地方
*
* @param state
*/
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
if (state == RecyclerView.SCROLL_STATE_IDLE
&& mLoadingListener != null && !isLoadingData && loadingMoreEnabled) {
LayoutManager layoutManager = getLayoutManager();
int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition =
((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] into =
new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
lastVisibleItemPosition = findMax(into);
} else {
lastVisibleItemPosition =
((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
}
if (layoutManager.getChildCount() > 0
&& lastVisibleItemPosition >= layoutManager.getItemCount() - 1
&& layoutManager.getItemCount() > layoutManager.getChildCount()
&& !isnomore) {
View footView = mFootViews.get(0);
isLoadingData = true;
if (footView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) footView).setState(
LoadingMoreFooter.STATE_LAODING);
} else {
footView.setVisibility(View.VISIBLE);
}
mLoadingListener.onLoadMore();
// 一个回调接口,用来加载数据
}
}
}

      写到这个地方,基本的上拉加载的逻辑就搞定了,然后就是处理细节。我们需要把底部布局LoadingMoreFooter加载到RecyclerView,这时候重写setAdapter(Adapter adapter)方法来添加一个adapter。

    5. 自定义一个adapter来把底部布局加进去。自定义的Adapter如下:

   private class WrapAdapter extends RecyclerView.Adapter<ViewHolder> {
private RecyclerView.Adapter adapter;
private ArrayList<View> mFootViews;
private int headerPosition = 0; public WrapAdapter(ArrayList<View> footViews, RecyclerView.Adapter adapter) {
this.adapter = adapter;
this.mFootViews = footViews;
} @Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return (isFooter(position)) ? gridManager.getSpanCount() : 1;
}
});
}
} @Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams && (isFooter(holder.getLayoutPosition()))
{
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);
}
} public boolean isFooter(int position) {
return position < getItemCount() && position >= getItemCount() - mFootViews.size();
} public int getFootersCount() {
return mFootViews.size();
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_FOOTER) {
return new SimpleViewHolder(mFootViews.get(0));
}
return adapter.onCreateViewHolder(parent, viewType);
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeader(position)) {
return;
}
int adjPosition = position;
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
adapter.onBindViewHolder(holder, adjPosition);
return;
}
}
} @Override
public int getItemCount() {
if (adapter != null) {
return getFootersCount() + adapter.getItemCount();
} else {
return getFootersCount();
}
} @Override
public int getItemViewType(int position) {
if (isFooter(position)) {
return TYPE_FOOTER;
}
int adjPosition = position;
int adapterCount;
if (adapter != null) {
adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
return adapter.getItemViewType(adjPosition);
}
}
return TYPE_NORMAL;
} @Override
public long getItemId(int position) {
if (adapter != null) {
int adjPosition = position - getHeadersCount();
int adapterCount = adapter.getItemCount();
if (adjPosition < adapterCount) {
return adapter.getItemId(adjPosition);
}
}
return -1;
} private class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
}

        就是一个继承自RecyclerView.Adapter的adapter,主要用于根据类型加载不同的布局,普通的itemView和“正在加载”的底部提示。 定义一个mDataObserver

     6. 回到setAdapter(Adapter adapter)方法

   /**
* 重写Adapter,通过状态判断是否显示“正在加载”
*
* @param adapter
*/
@Override
public void setAdapter(Adapter adapter) {
this.mAdapter = adapter;
this.mWrapAdapter = new WrapAdapter(mFootViews, mAdapter);// 定义WrapAdapter
super.setAdapter(mWrapAdapter);// 通过父类方法将自定义的Adapter重新设置进去
mAdapter.registerAdapterDataObserver(mDataObserver);//请看下面分析
}

    查看super.setAdapter()方法,会找到adapter.registerAdapterDataObserver(mObserver)方法,当adapter里面的数据发生改变时会即时监听并且更新。

    那为什么还要把mAdapter再设置一遍呢?其实当我们调用通过我们自定义的RecyclerView来调用setAdapter方法时,只有当WrapAdapter数据改变的时候,才会有更新,而当我们仅仅只更新mAdapter里面的数据的时候,如果不监听,我们看到的itemView并没有改变。

    7.定义一个mDataObserver

private final RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
mWrapAdapter.notifyDataSetChanged();
} @Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount);
} @Override
public void onItemRangeChanged(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount);
} @Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
} @Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount);
} @Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mWrapAdapter.notifyItemMoved(fromPosition, toPosition);
}
};

    都是直接调用父类的方法就可以。

  这样一个RecyclerView的上拉加载逻辑就全部搞定了,这是极其简单的封装方法,所以逻辑没有多么的复杂。

  好的,来回顾一下逻辑:重写RecyclerView进行滑动监听,当滑动到底部的时候通过重写setAdapter来将底部视图加载出来,最后对mAdapter进行数据更改的监听。

  上拉刷新的逻辑更简单,因为有谷歌的SwipeRefreshLayout,所以实现起来就简单很多,首先来看布局文件

<android.support.v4.widget.SwipeRefreshLayout
  android:id="@+id/swipe"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"/>
<com.baiyyyhjl.pullrecyclerview.recyclerview.XRecylcerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.SwipeRefreshLayout>

  直接用SwipeRefreshLayout将我们刚才自定义的RecyclerView包裹起来,然后swipeRefreshLayout.setOnRefreshListener(this)进行监听,实现onRefresh()接口来实现加载的逻辑。

  就这样,一个简单实用的RecyclerView上拉加载,下拉刷新就实现了。没有多余的布局文件,极其简便。

  当我们项目中有多个RecyclerView并且要求上拉加载,下拉刷新的时候,我们可以定义一个抽象类,只通过修改itemView的布局就能实现。

  

RecyclerView 下拉刷新上拉加载的更多相关文章

  1. Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView

    在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/de ...

  2. SwipeRefreshLayout实现下拉刷新上滑加载

    1. 效果图 2.RefreshLayout.java package myapplication.com.myapplication; import android.content.Context; ...

  3. 移动端下拉刷新上拉加载-mescroll.js插件

    最近无意间看到有这么一个上拉刷新下拉加载的插件 -- mescroll.js,个人感觉挺好用的,官网地址是:http://www.mescroll.com 然后我就看了一下文档,简单的写了一个小dem ...

  4. 带你实现开发者头条APP(五)--RecyclerView下拉刷新上拉加载

    title: 带你实现开发者头条APP(五)--RecyclerView下拉刷新上拉加载 tags: -RecyclerView,下拉刷新,上拉加载更多 grammar_cjkRuby: true - ...

  5. RecyclerView下拉刷新上拉加载(三)—对Adapter的封装

    RecyclerView下拉刷新上拉加载(一) http://blog.csdn.net/baiyuliang2013/article/details/51506036 RecyclerView下拉刷 ...

  6. RecyclerView下拉刷新上拉加载(二)

    listview下拉刷新上拉加载扩展(一) http://blog.csdn.net/baiyuliang2013/article/details/50252561 listview下拉刷新上拉加载扩 ...

  7. RecyclerView下拉刷新上拉加载(一)

    listview下拉刷新上拉加载扩展(一) http://blog.csdn.net/baiyuliang2013/article/details/50252561 listview下拉刷新上拉加载扩 ...

  8. MaterialRefreshLayout+ListView 下拉刷新 上拉加载

    效果图是这样的,有入侵式的,非入侵式的,带波浪效果的......就那几个属性,都给出来了,自己去试就行. 下拉刷新 上拉加载 关于下拉刷新-上拉加载的效果,有许许多多的实现方式,百度了一下竟然有几十种 ...

  9. 自定义ListView下拉刷新上拉加载更多

    自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...

  10. ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多

    ListView实现Item上下拖动交换位置  并且实现下拉刷新  上拉加载更多 package com.example.ListViewDragItem; import android.app.Ac ...

随机推荐

  1. 关于Latent Dirichlet Allocation

    今天,也没出去,晚上宿舍没有人,自己思考了下人生,毕设还是大事,觉得现在有必要把LDA从前往后彻彻底底的读一遍了,因为现在的感觉就是什么都知道一点皮毛,但是理解的都不深,LDA好像(恩,相当不好)现在 ...

  2. 15个必须知道的chrome开发者技巧(转)

    15个必须知道的chrome开发者技巧 在Web开发者中,Google Chrome是使用最广泛的浏览器.六周一次的发布周期和一套强大的不断扩大开发功能,使其成为了web开发者必备的工具.你可能已经熟 ...

  3. 【转】Android 服务器之SFTP服务器上传下载功能

    原文网址:http://blog.csdn.net/tanghua0809/article/details/47056327 本文主要是讲解Android服务器之SFTP服务器的上传下载功能,也是对之 ...

  4. 纠结的ARC

    xcode不断进步,在xcode4中引入了ARC的概念.您用或者不用它就在那里,于是有了本文:如何在未使用arc的工程中引入一个使用了arc特性的文件,如何在arc工程中引用未使用arc的文件.其实说 ...

  5. 《C++ primer》--第1,2章小结

    来源:http://blog.csdn.net/wangqiulin123456/article/details/8483853 1.变量初始化:      定义变量时,应该给变量赋初始值,除非确定将 ...

  6. [python]使用pexpect模块进行批量scp

    #!/usr/bin/env python# -*- coding: utf-8 -*- #wangxiaofei #awcloud自动化测试 import time,osimport threadi ...

  7. NewtonPrincipia --- 公理或运动的定律 --- 系理二

    NewtonPrincipia --- 公理或运动的定律 --- 系理二 自然哲学的数学原理>公理或运动的定律>系理II 平行四边形ABCD,那么:直接的力AD由任意的力AB和BD合成,直 ...

  8. nagios-解决监控页面上的乱码

    1. 前景 在nagios的监控页面上发现返回来的信息为乱码,如下图所示: 查看相关日志,发现正常显示汉字,如下: 2. 解决方法(以下两个步骤缺一不可) 主要原因分析如下: 在nagios服务器上发 ...

  9. C++设计模式——单例模式

    问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进 ...

  10. 转 毛笔字教程ps

    跟大家分享一下毛笔字怎么做出来的,主要通过字体和素材叠加,十分简单,喜欢的一起练习.做完记得交作业. 先看看最终效果: 在网上是不是经常看这些碉堡了的毛笔感觉是不是很羡慕啊,现在我就教大家怎么做出这样 ...