RecyclerView 下拉刷新和上拉加载

RecyclerView 下拉刷新和上拉加载

RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情。

在ListView上,我们可以通过自己添加addHeadViewaddFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需要自己实现上拉加载。

而在下面我们将用两种方式来实现上拉加载和下拉刷新

  • SwipeRefreshLayout+滑动底部自动加载

  • 使用第三方库SwipeToLoadLayout实现上拉加载和下拉刷新。

SwipeRefreshLayout+滑动底部自动加载

SwipeRefreshLayout实现很简单,重点是滑动到底部自动加载应该如何实现,其实其实现的方式类似于ListView的实现方式。

看一下activity_recycle_swiperefresh.xml文件:

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_refresh"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/swipe_target"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none" />

</android.support.v4.widget.SwipeRefreshLayout>

布局文件就两个控件,SwipeRefreshLayout中嵌套RecyclerView

在代码中初始化RecyclerView以及实现adapter等,这不是重点,不再贴代码。

RecyclerView中有方法addOnScrollListener,该方法类似于ListViewsetOnScrollListener方法,OnScrollListener中有两个方法的回调

  • onScrolled(RecyclerView recyclerView, int dx, int dy) :滚动的回调,dx和dy表示手指滑动水平和垂直的偏移量。

  • onScrollStateChanged(RecyclerView recyclerView, int newState):滑动状态的回调。

那么,我们的着重点就在这个两个方法上了。

对于向上加载更多,我们需要有如下判断

  • 是否是向上滑动
  • 是否滑动到底部
  • 当前是否正在加载数据
  • 当前状态是否是滑动停止的状态

实现比较复杂,定义一个类LoadDataScrollController,继承类RecyclerView.OnScrollListener

因为onScrollStateChanged实在状态改变时的回调,无法时时的获取显示的条目以及位置,所以我们在onScrolled中获取相应位置,

  @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

        /**
         * 获取布局参数
         */
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

        //如果为null,第一次运行,确定布局类型
        if (mLayoutManagerType == null) {
            if (layoutManager instanceof LinearLayoutManager) {
                mLayoutManagerType = LayoutManagerType.LINEAR_LAYOUT;
            } else if (layoutManager instanceof GridLayoutManager) {
                mLayoutManagerType = LayoutManagerType.GRID_LAYOUT;
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                mLayoutManagerType = LayoutManagerType.STAGGERED_GRID_LAYOUT;
            } else {
                throw new RuntimeException("LayoutManager should be LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager");
            }
        }

        //对于不太能够的布局参数,不同的方法获取到当前显示的最后一个条目数
        switch (mLayoutManagerType) {
            case LINEAR_LAYOUT:
                mLastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                break;
            case GRID_LAYOUT:
                mLastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
                break;
            case STAGGERED_GRID_LAYOUT:
                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                if (mLastPostions == null) {
                    mLastPostions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
                staggeredGridLayoutManager.findLastVisibleItemPositions(mLastPostions);
                mLastVisibleItemPosition = findMax(mLastPostions);
                break;
            default:
                break;
        }

    }

首先获取布局管理器,并判断是那种类型的,因为有三种类型,定义枚举来保存布局类型的参数

/**
 *
 * RecycleView的布局管理器的类型
 * Created by Alex_MaHao on 2016/5/10.
 */
public enum  LayoutManagerType {
    LINEAR_LAYOUT,
    GRID_LAYOUT,
    STAGGERED_GRID_LAYOUT
}

然后根据布局惯例其的类型获取其当前显示的最大条目,对于瀑布流来说,他如果是垂直的两列瀑布的话,我们需要获取两列中分别最大条目数,进行比较,选出最大条目数。

   /**
     * 当是瀑布流时,获取到的是每一个瀑布最下方显示的条目,通过条目进行对比
     */
    private int findMax(int[] lastPositions) {
        int max = lastPositions[0];
        for (int value : lastPositions) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }

拿到当前最大的条目数之后,在onScrollStateChange中进行判断状态等,

 @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

        //RecycleView 显示的条目数
        int visibleCount = layoutManager.getChildCount();

        //显示数据总数
        int totalCount = layoutManager.getItemCount();


        // 四个条件,分别是是否有数据,状态是否是滑动停止状态,显示的最大条目是否大于整个数据(注意偏移量),是否正在加载数据
        if(visibleCount>0
                &&newState==RecyclerView.SCROLL_STATE_IDLE
                &&mLastVisibleItemPosition>=totalCount-1
                &&!isLoadData){
            //可以加载数据
           isLoadData = true;
        }

    }

注释很清楚,在加载数据的地方,我们将isLoadData设为true,同时利用接口回调加载数据,等数据加载完成,通过setLoadDataStatus方法设置为false

   public void setLoadDataStatus(boolean isLoadData){
        this.isLoadData = isLoadData;
    }

如果这样就结束了,感觉很麻烦,对于刷新和加载更多,我们需要在调用的地方分别设置监听,那么我们可以让LoadDataScrollController实现SwipeRefreshLayout的刷新监听方法,在利用我们定义的统一的上拉刷新和加载数据接口进行处理

/**
 * 实现上拉加载的监听:加载条件:滑动到最后,且是停止状态,则开始加载数据
 * Created by Alex_MaHao on 2016/5/10.
 */
public  class LoadDataScrollController extends RecyclerView.OnScrollListener implements SwipeRefreshLayout.OnRefreshListener {
   

    /**
     * 当前布局管理器的类型
     */
    private LayoutManagerType mLayoutManagerType;


    /**
     * 当前RecycleView显示的最大条目
     */
    private int mLastVisibleItemPosition;


    /**
     * 每列的最后一个条目
     */
    private int[] mLastPostions;


    /**
     * 是否正在加载数据  包括刷新和向上加载更多
     */
    private boolean isLoad
  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值