我们之前在使用 PullToRefreshListView 的时候养成了一个习惯:上拉可以在列表页的下面出现一个 footer,上面显示上拉加载,放手之后会回调到我们的方法中,这个时候我们可以去请求数据,然后把获得的数据添加到 List 中,更新列表中的数据。下拉的时候上面会出现一个 header,上面显示下拉刷新,放手后也可以进入我们的回调方法。如下图(网图,侵删)
后来我们有了更方便使用的 RecyclerView,介绍以及使用请点击,但是我们熟悉的上拉加载、下拉刷新不见了。
再后来我们想到了 SwipeRefreshLayout,这个控件在下拉的时候可以下拉出一个圆,会一直转啊转,这个控件号称兼容一切。使用的话网上随便搜一搜就有很多。
现在我们缺个上拉加载的,有很多种方法:
http://blog.csdn.net/u012036813/article/details/38959507
或者使用我这种方法,借用大神的 PullToRefresh 中的一些类来做:
开始制作
第一步,首先我们要把 PullToRefresh 集成到我们的项目中, 然后我们只要新建一个 class 继承 PullToRefreshBase,并实现里面的四个方法就可以了
实现 PullToRefreshBase 中的四个方法
@Override public Orientation getPullToRefreshScrollDirection() { return null; } @Override protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) { return null; } @Override protected boolean isReadyForPullEnd() { return false; } @Override protected boolean isReadyForPullStart() { return false; }
这四个方法分别是做什么用的,来介绍一下
getPullToRefreshScrollDirection 用来设置滑动的方向
createRefreshableView 用来设置里面滑动的 view
isReadyForPullEnd 判断是否滑动到底
isReadyForPullStart 判断是否滑动到顶
这里着重说下网上的一种写法:
//重写 4 个方法 //3 是否滑动到底部 @Override protected boolean isReadyForPullEnd() { View view = getRefreshableView().getChildAt(getRefreshableView().getChildCount() - 1); if (null != view) { return getRefreshableView().getBottom() >= view.getBottom(); } return false; }
这种写法是有问题的,在使用的时候就会发现,上拉获得数据并 notifyDataSetChanged 之后,再上拉仍然会把上拉加载的 view 拉出来,并不能上拉看到刚刚获得的数据,必须先下拉一下才可以。这个问题主要是因为获得的 view 是当前界面上的最后一个 view,假如已经下拉到底部的时候,他们俩的值一直是相等的,所以一直会出现上拉加载的的那个 view。
这个问题的解决方法有很多:第一种是使用
protected boolean isReadyForPullEnd() { View view = getRefreshableView().getLayoutManager().getChildAt(getRefreshableView().getChildCount() - 1); if (null != view) { RecyclerView.Adapter adapter = getRefreshableView().getAdapter(); if (adapter!=null){ if (adapter.getItemCount()-1!=getRefreshableView().getChildAdapterPosition(view)){ return false; } } return getRefreshableView().getBottom() >= view.getBottom(); } return false; }
第二行代码可以获得当前页面的最后一个元素的 positon,我们可以用这个 Postion 对比从 adapter 获得的 item 的个数,如果是相同的话,才能确定这个元素是最后一个元素,才正真应该开启上拉,所以这个时候可以返回 true
或者使用第二种方法
protected boolean isReadyForPullEnd() { View view = getRefreshableView().getLayoutManager().getChildAt(getRefreshableView().getChildCount() - 1); if (null != view) { RecyclerView.Adapter adapter = getRefreshableView().getAdapter(); if (adapter!=null){ if (adapter.getItemCount()-1!=getRefreshableView().getLayoutManager().getPosition(view)){ return false; } } return getRefreshableView().getBottom() >= view.getBottom(); } return false; }
下面放出这个类的全部代码
package net.sumile.sumileswiprecyview; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.Adapter; import com.handmark.pulltorefresh.library.PullToRefreshBase; /** * Created by sumile on 2016/8/5. */ public class SumileRecyclerView extends PullToRefreshBase { public SumileRecyclerView(Context context) { super(context); } public SumileRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public SumileRecyclerView(Context context, Mode mode) { super(context, mode); } public SumileRecyclerView(Context context, Mode mode, AnimationStyle animStyle) { super(context, mode, animStyle); } //重写 4 个方法 //1 滑动方向 @Override public Orientation getPullToRefreshScrollDirection() { return Orientation.VERTICAL; } //重写 4 个方法 //2 滑动的 View @Override protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) { RecyclerView view = new RecyclerView(context, attrs); return view; } //重写 4 个方法 //3 是否滑动到底部 @Override protected boolean isReadyForPullEnd() { View view = getRefreshableView().getLayoutManager().getChildAt(getRefreshableView().getChildCount() - 1); if (null != view) { RecyclerView.Adapter adapter = getRefreshableView().getAdapter(); if (adapter!=null){ if (adapter.getItemCount()-1!=getRefreshableView().getChildAdapterPosition(view)){ return false; } } return getRefreshableView().getBottom() >= view.getBottom(); } return false; } //重写 4 个方法 //4 是否滑动到顶部 @Override protected boolean isReadyForPullStart() { // View view = getRefreshableView().getChildAt(0); // // if (view != null) { // return view.getTop() >= getRefreshableView().getTop(); // } return false; } }
因为我这里要做的是下面使用 pullToRefresh 的上拉,使用 swip 的下拉,所以 isReadyForPullStart 中我就直接返回 false 了,完成这个之后,我们的这个 view 已经具有了 PullToRefresh 的上拉下拉功能.
现在给我们的 view 添加 SwipeRefreshLayout
这里我实现了一些简单的封装,直接在构造方法通过 view 中的 addView 把 recyclerView 添加进去
new RecyclerView 然后 addView 就可以
下面放出我封装的包含有 SwipeRefreshLayout、PullToRefresh 以及 RecyclerView 的类
package net.sumile.sumileswiprecyview; import android.content.Context; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.widget.LinearLayout; import com.handmark.pulltorefresh.library.PullToRefreshBase; /** * Created by sumile on 2016/8/2. */ public class SwipRecyView extends SwipeRefreshLayout { public SwipRecyView(Context context) { this(context, null); } private SumileRecyclerView sumileRecyclerView; private RecyclerView recyclerView; private SwipRecyView swipRecyView; private Context mContext; public SwipRecyView(Context context, AttributeSet attrs) { super(context, attrs); swipRecyView = this; sumileRecyclerView = new SumileRecyclerView(context); recyclerView = sumileRecyclerView.getRefreshableView(); LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext); recyclerView.setLayoutManager(mLayoutManager); addView(sumileRecyclerView); mContext = context; } public RecyclerView getRefreshableView() { return sumileRecyclerView.getRefreshableView(); } public SumileRecyclerView getSumileRecyclerView() { return sumileRecyclerView; } private void initAction(final OnSwipRecyListener callBack) { if (callBack != null) { OnRefreshListener listener = new OnRefreshListener() { @Override public void onRefresh() { callBack.onPullDownToRefresh(sumileRecyclerView); } }; setOnRefreshListener(listener); sumileRecyclerView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2() { @Override public void onPullDownToRefresh(PullToRefreshBase refreshView) { } @Override public void onPullUpToRefresh(PullToRefreshBase refreshView) { callBack.onPullUpToRefresh(refreshView); } }); } } public void setRefreshing(boolean show) { super.setRefreshing(show); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); } private SumileRecyclerView initSwipView() { addView(sumileRecyclerView); return sumileRecyclerView; } //设置 swipview 的默认值 private void setDefaultSwipRecyView() { setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light); } private void setDefaultSumileRecyclerView() { sumileRecyclerView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); sumileRecyclerView.setMode(PullToRefreshBase.Mode.PULL_FROM_END); sumileRecyclerView.getLoadingLayoutProxy(false, true).setPullLabel("上拉加载"); } private RecyclerView setDefaultRecyclerView() { LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(mLayoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); return recyclerView; } public void onRefreshComplete() { sumileRecyclerView.onRefreshComplete(); setRefreshing(false); } public interface OnSwipRecyListener { void onPullUpToRefresh(PullToRefreshBase refreshView); void onPullDownToRefresh(SumileRecyclerView sumileRecyclerView); } public void setSwipRecyViewBuilder(SwipRecyViewBuilder builder) { if (builder.mSwipRecyView != null) { } else { setDefaultSwipRecyView(); } if (builder.mCallBack != null) { initAction(builder.mCallBack); } if (builder.mSumileRecyclerView != null) { } else { setDefaultSumileRecyclerView(); } if (builder.mRecyclerView != null) { } else { setDefaultRecyclerView(); } recyclerView.setAdapter(builder.mAdapter); } public static class SwipRecyViewBuilder { public OnSwipRecyListener mCallBack; public SumileRecyclerView mSumileRecyclerView; public RecyclerView mRecyclerView; public SwipRecyView mSwipRecyView; public SwipRecyView mSwipRecyViewOld; public RecyclerView.Adapter mAdapter; public SwipRecyViewBuilder(SwipRecyView mSwipRecyView, RecyclerView.Adapter adapter) { this.mAdapter = adapter; this.mSwipRecyViewOld = mSwipRecyView; } public SwipRecyViewBuilder setOnSwipRecyRefreshListener(OnSwipRecyListener listener) { this.mCallBack = listener; return this; } public SwipRecyViewBuilder initPullToRefreshView(InitPullToRefreshView sumileRecyclerViewInterface) { this.mSumileRecyclerView = sumileRecyclerViewInterface.initSumileRecyclerView(mSwipRecyViewOld.getSumileRecyclerView()); return this; } public SwipRecyViewBuilder initRecycleView(InitRecyclerView recyclerViewInterface) { this.mRecyclerView = recyclerViewInterface.initRecyclerView(mSwipRecyViewOld.getRefreshableView()); return this; } public SwipRecyViewBuilder initSwipRecyView(InitSwipRecyView swipRecyViewInterface) { this.mSwipRecyView = swipRecyViewInterface.initSwipRecyView(mSwipRecyViewOld); return this; } } public interface InitPullToRefreshView { public SumileRecyclerView initSumileRecyclerView(SumileRecyclerView sumileRecyclerView); } public interface InitSwipRecyView { SwipRecyView initSwipRecyView(SwipRecyView swipRecyView); } public interface InitRecyclerView { RecyclerView initRecyclerView(RecyclerView recyclerView); } }
使用的时候只要在 layout 里面是用下面的代码就可以
<net.sumile.sumileswiprecyview.SwipRecyView android:id="@+id/srView" android:layout_width="match_parent" android:layout_height="match_parent"> </net.sumile.sumileswiprecyview.SwipRecyView>
而在代码中,这样使用
srView = (SwipRecyView) view.findViewById(R.id.srView); SwipRecyView.SwipRecyViewBuilder builder = new SwipRecyViewBuilder(srView, adapter); builder.setOnSwipRecyRefreshListener(new OnSwipRecyListener() { @Override public void onPullUpToRefresh(PullToRefreshBase refreshView) { getDataWithPage(++page); } @Override public void onPullDownToRefresh(SumileRecyclerView sumileRecyclerView) { getDataWithPage(1); } }).initSwipRecyView(new InitSwipRecyView() { @Override public SwipRecyView initSwipRecyView(SwipRecyView swipRecyView) { swipRecyView.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_green_light); return swipRecyView; } }); srView.setSwipRecyViewBuilder(builder);
Builder 中共有四个方法,一个设置上拉下拉的回调,另外三个分别是设置 SwipeRefreshLayout:InitSwipRecyView、PullToRefresh:InitPullToRefreshView、RecyclerView:InitRecyclerView 的,在获得到这三个控件之后,可以对它设置,然后将它返回就行,如果返回为空,则使用默认的设置,比较简陋。(其实返回的时候只要不是空就行)
项目的 github 地址:https://github.com/wudkj/SumileSwipRecyView
转载请注明:热爱改变生活.cn » 为 RecyclerView 添加上拉刷新及下拉加载(解决上拉之后必须先下拉一下才能继续下拉的问题)
本博客只要没有注明“转”,那么均为原创。 转载请注明链接:sumile.cn » 为 RecyclerView 添加上拉刷新及下拉加载(解决上拉之后必须先下拉一下才能继续下拉的问题)