一、背景
很久很久以前简单封装过一次RecyclerView,后来一直开发TV端,列表的需求花样不是很多,主要是解决TV端各种兼容问题,也没怎么琢磨这个美丽的控件了,现在打算重新整理一下,其实网上已经有很多优秀的开源项目了,涵盖功能多,但是就因为功能太多,用起来反而有一些不方便的地方,例如用在TV上或者别的什么地方,有的地方得根据需求和兼容问题作出修改,这样改起来就麻烦了,看的头皮发麻呀,而且很多功能用不到,用第三方库时,你们是不是也是多一个功能都不想要,所以还是自己搞一下,可以方便的扩展和修改,最终效果:


先说下之前的,RecyclerView下拉刷新很简单,直接通过其外层包裹的SwipeRefreshLayout提供的接口即可实现,然后我就自然而然的像自定义其它控件一样把这两个控件给自定义成了一个组合控件,使用时如下:

    <com.wasu.tvfc.widget.ARecycleViewandroid:id="@+id/ac_recycler"android:layout_width="wrap_content"android:layout_height="match_parent"app:EmptyView="@layout/empty_view"app:layout_moreProgress="@layout/view_more_progress" />
mAdapter = new WechatAdapter(mContext, mList);
recyclerview.setLayoutManager(new LinearLayoutManager(mContext,LinearLayoutM
recyclerview.setAdapter(mAdapter);
recyclerview.setOnLoadMoreListener(new ARecycleView.OnLoadMoreListener() {@Overridepublic void loadMore() {mPresenter.getMoreWechatData();}
});
recyclerview.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()@Overridepublic void onRefresh() {mPresenter.getWechatData();}
});

之前感觉这样用起来很方便了,但是随着时代的进步,祖国的发展,人民生活水平不断提高,房价也越来越贵了,想生活的简单点的我,越来越觉得之前那种写法用起来不是很舒服,因此对自己提出了几点疑问:
Q:为何要把 SwipeRefreshLayout 和 RecyclerView 封装到一起?
A:当时需求不是很多,在布局的写法也基本一样,所以这种一堆控件不如写成一个组合控件用的方便;
Q:这其实是一种惯性思维,为何要把数据为空的展示逻辑也封装到这个组合控件里了?
A:之前用 ListView 时也经常有列表为空的展示需求,现在自定义 RecyclerVIew 就干脆把这个功能也一起封装到这个组合控件里,让其功能尽量完善;
Q:这样一来其它不包含列表的页面数据为空时如果也要显示一个特殊页,就得再写一套空展示逻辑,这就和 RecyclerView 里的重复了,这不成了形而上学了,现在我想要一个纯净的写法,还要能实现上拉加载更多和下拉刷新,就像这样:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".activity.ListMoreActivity"><android.support.v4.widget.SwipeRefreshLayoutandroid:id="@+id/swiperefreshlayout"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/recyclerview"android:layout_width="match_parent"android:layout_height="match_parent"></android.support.v7.widget.RecyclerView></android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>

总结:
1.别的控件为了使用方便可以自定义成一个组合控件,但是 SwipeRefreshLayout 和 RecyclerView 定义到一起感觉有点不爽;
2.empty 页面直接定义到 RecyclerView 里其实不是一种完美的做法,会有很多局限,例如其它没有用到 RecyclerView 的页面如果也要在无数据时展示 empty 页面,这时又得重新搞一套逻辑,所以这种特殊页面应该单独搞一个模块,本文暂不涉及;
3.要在布局里不改变原生写法的情况下实现加载更多和下拉刷新逻辑。

二.方案
既然不能在封装 RecyclerView 和 SwipeRefreshLayout 上做文章了,那么就只好去找 Adapter 看看了,思索了许久终于想到了两个方案;(什么?你说我是看了github 上的开源项目才想到的?就你知道的多)
2.1方案一:Adapter 和布局都以原生的方式来写,然后把原生的 Adapter 装饰一把,增加上拉加载更多的逻辑,加载判断等逻辑放到一个单独工具类里;
思路如下:
1.布局及 adapter 都以原生的方式来写;
2.装饰 adapter 的功能,通过 itemType 区分使用哪种 ViewHolder,从而区分展示内容布局,加载更多布局什么的;
3.再用一个单独的工具类来处理加载更多的监听判断逻辑;
Activity的xml:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".activity.ListMoreActivity"><android.support.v4.widget.SwipeRefreshLayoutandroid:id="@+id/swiperefreshlayout"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/recyclerview"android:layout_width="match_parent"android:layout_height="match_parent"></android.support.v7.widget.RecyclerView></android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>

adapter:

public class ListMoreAdapter extends RecyclerView.Adapter<ListMoreAdapter.MyViewHolder> {private List<String> mList;public ListMoreAdapter(List<String> list) {this.mList = list;}@NonNull@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false));}@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {holder.tv.setText(mList.get(position));}@Overridepublic int getItemCount() {return mList.size();}class MyViewHolder extends RecyclerView.ViewHolder {private TextView tv;private MyViewHolder(View itemView) {super(itemView);tv = itemView.findViewById(R.id.textview);}}
}

Activity里使用:

public class ListMoreActivity extends BaseActivity {private static Handler mHandler = new Handler();private ALoadMoreAdapterWrapper aLoadMoreAdapterWrapper;@Overrideprotected void init() {initData();}private void initData() {ListMoreAdapter listMoreAdapter = new ListMoreAdapter(mDatas);aLoadMoreAdapterWrapper = new ALoadMoreAdapterWrapper(listMoreAdapter);//装饰AdapterrecyclerView.setAdapter(aLoadMoreAdapterWrapper);recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));ARecyclerViewHelper aRecyclerViewHelper = new ARecyclerViewHelper(recyclerView, aLoadMoreAdapterWrapper);//工具类aRecyclerViewHelper.setOnLoadMoreListener(new ARecyclerViewHelper.OnLoadMoreListener() {@Overridepublic void loadMore() {}});swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {}});}}

去掉 Activity 里的 14 和 18 行,就完全是没有任何杂质的原生写法了,包括布局 xml 和 Adapter,是不是很舒服 ,还有一个优点就是在 TV 或者别的什么一体机设备上万一出现问题,可以快速判断出是 RecyclerView 本身的兼容问题还是自定义 RecyclerView 里哪一段逻辑导致的问题,根据不同情况进行不同处理;
就通过这两行就可以增加上拉加载更多的功能,然后来看看这两行干了什么:
ALoadMoreAdapterWrapper.java

public class ALoadMoreAdapterWrapper extends RecyclerView.Adapter {private static final int ITEM_TYPE_LOADMOTE = 100001;private boolean enableLoadMore = true;private LoadMoreHolder mLoadMoreHolder;private RecyclerView.Adapter mAdapter;public ALoadMoreAdapterWrapper(RecyclerView.Adapter adapter) {this.mAdapter = adapter;}@NonNull@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {if (viewType == ITEM_TYPE_LOADMOTE) {if (null == mLoadMoreHolder) {mLoadMoreHolder = new LoadMoreHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_load_more, parent, false));setLoadMoreVisible(true);}return mLoadMoreHolder;}return mAdapter.onCreateViewHolder(parent, viewType);}@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {if (getItemViewType(position) != ITEM_TYPE_LOADMOTE) {mAdapter.onBindViewHolder(holder, position);}}@Overridepublic int getItemCount() {if (!enableLoadMore) {return mAdapter.getItemCount();}return mAdapter.getItemCount() + 1;}@Overridepublic int getItemViewType(int position) {if (!enableLoadMore) {return mAdapter.getItemViewType(position);}if (position == getItemCount() - 1) {return ITEM_TYPE_LOADMOTE;}return mAdapter.getItemViewType(position);}public void setLoadMoreEnable(boolean enable) {enableLoadMore = enable;setLoadMoreVisible(enable);notifyItemChanged(getLoadMoreViewPosition());}private int getLoadMoreViewPosition() {return getItemCount();}public boolean isEnableLoadMore() {return enableLoadMore;}private void setLoadMoreVisible(boolean flag) {if (null == mLoadMoreHolder) {return;}mLoadMoreHolder.setLoadMoreViewVisible(flag);}public void setLoadMoreState(boolean isLoadMore, boolean isFinish) {if (isFinish) {mLoadMoreHolder.setPbVisible(true);mLoadMoreHolder.setTv("-- 到底啦 --");mLoadMoreHolder.setBGColor();return;}if (isLoadMore) {mLoadMoreHolder.setTv("正在加载...");} else {mLoadMoreHolder.setTv("上拉加载更多...");}}class LoadMoreHolder extends RecyclerView.ViewHolder {private TextView tv;private View pb;LoadMoreHolder(View itemView) {super(itemView);tv = itemView.findViewById(R.id.ilm_tv);pb = itemView.findViewById(R.id.ilm_pb);itemView.setVisibility(View.GONE);}private void setTv(CharSequence txt) {tv.setText(txt);}private void setPbVisible(boolean isFinish) {pb.setVisibility(isFinish ? View.GONE : View.VISIBLE);}private void setLoadMoreViewVisible(boolean flag) {itemView.setVisibility(flag ? View.VISIBLE : View.GONE);}private void setBGColor() {itemView.setBackgroundColor(Color.parseColor("#FFF1EFF0"));}}
}

主要逻辑处理在于这几个方法:
getItemCount(),如果没开启加载更多功能就返回实际 count,否则返回实际的 count 加 1,这个1就是留着放加载更多布局的;
getItemViewType(),如果没开启加载更多功能就返回实际 adapter 里返回 type,否则最后一个 item 返回 TYPE_LOADMORE;
onCreateViewHolder(),如果 itemType 是 TYPE_LOADMORE,就创建 LoadMoreViewHolder,否则创建实际 adapter的ViewHolder;
onBindViewHolder(),如果 itemType 不是 TYPE_LOADMORE,就执行实际 adapter的onBindViewHolder。

ARecyclerViewHelper.java

public class ARecyclerViewHelper extends RecyclerView.OnScrollListener {private RecyclerView mRecyclerView;private RecyclerView.LayoutManager mLayoutManager;private ALoadMoreAdapterWrapper mAdapterWrapper;private boolean isLoading;private OnLoadMoreListener onLoadMoreListener;private boolean isFinish;private int lastItemCount;public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {this.onLoadMoreListener = onLoadMoreListener;mAdapterWrapper.setLoadMoreEnable(true);}public ARecyclerViewHelper(RecyclerView recyclerView, ALoadMoreAdapterWrapper adapterWrapper) {this.mRecyclerView = recyclerView;this.mAdapterWrapper = adapterWrapper;mLayoutManager = mRecyclerView.getLayoutManager();checkFullScreen(mRecyclerView);init();}private void init() {mRecyclerView.addOnScrollListener(this);}@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);if (!mAdapterWrapper.isEnableLoadMore()) {return;}if (newState == RecyclerView.SCROLL_STATE_IDLE) {if (isLoading) {return;}if (mLayoutManager instanceof GridLayoutManager) {final GridLayoutManager gridLayoutManager = (GridLayoutManager) mLayoutManager;gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {if (position == mLayoutManager.getItemCount() - 1) {return gridLayoutManager.getSpanCount();} else {return 1;}}});}int lastCompletelyVisibleItemPosition = 0;if (mLayoutManager instanceof LinearLayoutManager) {lastCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastCompletelyVisibleItemPosition();lastItemCount = mLayoutManager.getItemCount();if (!isFinish && lastCompletelyVisibleItemPosition == lastItemCount - 2) {//回弹效果int firstCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();View viewByPosition = mLayoutManager.findViewByPosition(lastCompletelyVisibleItemPosition);if (null == viewByPosition) {return;}int i = recyclerView.getBottom() - recyclerView.getPaddingBottom() - viewByPosition.getBottom();if (i > 0 && firstCompletelyVisibleItemPosition != 0) {recyclerView.smoothScrollBy(0, -i);}} else if (!isFinish && lastCompletelyVisibleItemPosition == lastItemCount - 1) {isLoading = true;mAdapterWrapper.setLoadMoreState(true, false);if (null != onLoadMoreListener) {onLoadMoreListener.loadMore();}}}}}/*** 加载完了调用此方法来关闭加载更多View或设置没有更多数据的View* @param isFinish*/public void setLoadMoreComplete(boolean isFinish) {isLoading = false;this.isFinish = isFinish;if (mLayoutManager.getItemCount() == lastItemCount) {//处理加载更多时没数据增加的情况,隐藏加载更多条目int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastCompletelyVisibleItemPosition();if (lastCompletelyVisibleItemPosition < lastItemCount - 2) {return;}int firstCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();View viewByPosition = mLayoutManager.findViewByPosition(lastItemCount - 2);int i = mRecyclerView.getBottom() - mRecyclerView.getPaddingBottom() - viewByPosition.getBottom();if (i > 0 && firstCompletelyVisibleItemPosition != 0) {mRecyclerView.smoothScrollBy(0, -i);}}mAdapterWrapper.setLoadMoreState(false, isFinish);}/*** 检测数据是否满屏* @param recyclerView*/private void checkFullScreen(RecyclerView recyclerView) {mAdapterWrapper.setLoadMoreEnable(false);if (recyclerView == null) return;final RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();if (manager == null) return;recyclerView.postDelayed(new Runnable() {@Overridepublic void run() {if (manager instanceof LinearLayoutManager) {LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;mAdapterWrapper.setLoadMoreEnable(isFullScreen(linearLayoutManager));} else if (manager instanceof StaggeredGridLayoutManager) {StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) manager;int[] positions = new int[staggeredGridLayoutManager.getSpanCount()];staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(positions);int pos = getTheBiggestNumber(positions) + 1;mAdapterWrapper.setLoadMoreEnable(pos != mAdapterWrapper.getItemCount());}}}, 50);}private boolean isFullScreen(LinearLayoutManager layoutManager) {return (layoutManager.findLastCompletelyVisibleItemPosition() + 1) != mAdapterWrapper.getItemCount() ||layoutManager.findFirstCompletelyVisibleItemPosition() != 0;}private int getTheBiggestNumber(int[] numbers) {int tmp = -1;if (numbers == null || numbers.length == 0) {return tmp;}for (int num : numbers) {if (num > tmp) {tmp = num;}}return tmp;}public interface OnLoadMoreListener {void loadMore();}
}

这个类的逻辑也不多,思路如下:
1.检测一下数据是否满屏了,如果没满就禁止加载更多,这个逻辑如果不处理,会出现只有一条数据时也显示加载更多 item 的情况,那就尴尬了;
2.设置 RecyclerView 的滑动监听,在 onScrollStateChanged() 方法里处理是否要加载更多的逻辑;
3.加载更多数据完成后,调用 setLoadMoreComplete() 方法处理 View 状态;
至此就已经实现了加载更多功能了。

有句话不知当讲不当讲,说了半天,其实我不喜欢这种方式,所以加载更多出错、点击重新加载什么的功能我也没在方案一代码里加。(什么?二狗子说要顺着网线来打我?且慢,让我说完方案二再来不迟,我还有更好的方案。)

2.2方案二:把控制上拉加载的逻辑放到一个 BaseAdapter 中,实际的 adapter 直接继承这个 BaseAdapter 就拥有了上拉加载功能;

public abstract class BaseAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {...public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {this.onLoadMoreListener = onLoadMoreListener;isOpenLoadMore = true;}public BaseAdapter(List<T> mDatas) {this.mDatas = mDatas;}protected abstract int getLayoutId();protected abstract void convert(BaseViewHolder viewHolder, T item);@NonNull@Overridepublic BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {BaseViewHolder baseViewHolder = null;switch (viewType) {case TYPE_CONTENT_VIEW:baseViewHolder = BaseViewHolder.create(getLayoutId(), parent);break;case TYPE_FOOTER_VIEW:if (mFooterLayout == null) {mFooterLayout = new RelativeLayout(parent.getContext());}baseViewHolder = BaseViewHolder.create(mFooterLayout);break;}return baseViewHolder;}@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {switch (holder.getItemViewType()) {case TYPE_CONTENT_VIEW:BaseViewHolder viewHolder = (BaseViewHolder) holder;convert(viewHolder, mDatas.get(position));break;}}@Overridepublic int getItemCount() {return mDatas.size() + getFooterViewCount();}@Overridepublic int getItemViewType(int position) {if (isFooterView(position)) {return TYPE_FOOTER_VIEW;}return TYPE_CONTENT_VIEW;}protected int getFooterViewCount() {return isOpenLoadMore && !mDatas.isEmpty() ? 1 : 0;}...
}

这是BaseAdapter里处理上拉加载的主要方法,也是通过 itemType 来区分加载什么 item,通过是否开启加载更多的 flag 来控制 itemCount,里面多处理了一个 ViewHolder 的逻辑,搞了一个 BaseViewHolder:

public class BaseViewHolder extends RecyclerView.ViewHolder {private final SparseArray<View> views;public BaseViewHolder(View itemView) {super(itemView);this.views = new SparseArray<>();}public static BaseViewHolder create(int layoutId, ViewGroup parent) {View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);return new BaseViewHolder(itemView);}public static BaseViewHolder create(View itemView) {return new BaseViewHolder(itemView);}@SuppressWarnings("unchecked")public <T extends View> T getView(@IdRes int viewId) {View view = views.get(viewId);if (view == null) {view = itemView.findViewById(viewId);views.put(viewId, view);}return (T) view;}public BaseViewHolder setText(@IdRes int viewId, CharSequence value) {TextView view = getView(viewId);view.setText(value);return this;}public BaseViewHolder setText(@IdRes int viewId, @StringRes int strId) {TextView view = getView(viewId);view.setText(strId);return this;}
}

示例代码只加了个 setText() 方法,增加其它方法和此方法基本相同,例如 setImg() 什么的。
实际使用:
ListMore2Adapter.java

public class ListMore2Adapter extends BaseAdapter<String> {public ListMore2Adapter(List<String> mDatas) {super(mDatas);}@Overrideprotected int getLayoutId() {return R.layout.item_list;}@Overrideprotected void convert(BaseViewHolder viewHolder, String item) {viewHolder.setText(R.id.textview, item);}
}

ListMore2Activity.java

private void initData() {listMoreAdapter = new ListMore2Adapter(mDatas);listMoreAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {@Overridepublic void onLoadMore(boolean isReload) {loadMore();}});//要在setAdapter之前设置OnLoadMoreListener,因为要通过设置Listener来标记加载更多功能开启,如果不这样做,就得在Adapter的构造函数里把标记传过去recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.setAdapter(listMoreAdapter);recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {}});}

这个方案就是文章最开始展示的效果了,其中有两点要注意的地方,GridView布局时,要自己写个ItemDecoration,需要对加载更多的item处理一下,否则会出现下面的情况

加载更多的layout右边被加上了空隙。

/*** Created by Aislli on 2018/9/10 0010.*/
public class SpaceDecoration extends RecyclerView.ItemDecoration {private int space;public SpaceDecoration(int space) {this.space = space;}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {super.getItemOffsets(outRect, view, parent, state);RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();int spanCount;int orientation;int itemCount = layoutManager.getItemCount();boolean isLoadMore;if (layoutManager instanceof GridLayoutManager) {GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;spanCount = gridLayoutManager.getSpanCount();orientation = gridLayoutManager.getOrientation();GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();isLoadMore = lp.getSpanSize() == spanCount;} else {StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;spanCount = staggeredGridLayoutManager.getSpanCount();orientation = staggeredGridLayoutManager.getOrientation();StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();isLoadMore = lp.isFullSpan();}int childAdapterPosition = parent.getChildAdapterPosition(view);if (orientation == GridLayoutManager.VERTICAL) {outRect.top = childAdapterPosition < spanCount ? 0 : space;// the first rowoutRect.bottom = 0;outRect.left = 0;if (childAdapterPosition % spanCount < (spanCount - 1)) {// except the last columnoutRect.right = space;}if (childAdapterPosition == itemCount - 1 && isLoadMore) {// load more viewoutRect.right = 0;}}}
}

第 44 行这里处理一下最后一个 item 的 right 值就 OK 了。
第二个需要注意的是,如果一行有 4 个 item,结果产品要求上拉加载时每页数据少于 4 条,例如要求一页加载 3 条数据,这样会出现一个问题


当加载后数据正好充满一行时,这个加载更多的 View 不会触发 onScrollStateChanged() 方法,那么就不会执行弹回判断操作,从而导致加载更多 View 回不去了,不过理论上不会有这样的需求的,一行 4 条数据,加载更多时一页最少也要 4 条数据才有意义,所以正常情况下是不会出现这种情况的。但是如果真的有这种需求也没关系,加个判断处理一下就是

    /*** 重置成正常状态*/private void stateDefault() {currentState = STATE_DEFAULT;mLoadingViewTv.setText("上拉加载更多");mLoadingViewPb.setVisibility(View.GONE);
//        mRecyclerView.postDelayed(new Runnable() {//            @Override
//            public void run() {//                hiddenFooterView();
//            }
//        }, 200);}private void hiddenFooterView() {RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();int lastVisibleItemPosition = findLastVisibleItemPosition(layoutManager);if (lastVisibleItemPosition >= getItemCount() - 2 && currentState != STATE_END) {if (findFirstVisibleItemPosition(layoutManager) == 0) return;View viewByPosition = layoutManager.findViewByPosition(getItemCount() - 2);if (null == viewByPosition) return;int i = mRecyclerView.getBottom() - mRecyclerView.getPaddingBottom() - viewByPosition.getBottom();if (i > 0) {mRecyclerView.smoothScrollBy(0, -i);}}}

正常加载完都会触发 stateDefault() 方法重置状态,如果有上面所说需求,就把上面的注释放开,在重置完状态后执行 hiddenFooterView() 就 OK 了,没这种需求就不用加这个检测了,一般不会出现这种情况的。
后面的同学突然大吼一声:嘿!!同学,你仄条嗦的有问题吧,“一行 4 条数据,加载更多时一页最少也要 4 条数据才有意义”,什么叫赠藏情况下不会粗现仄种情况,如果最后一页就剩下 2 条数据怎么办,每次都可能粗现呀!你嗦怎么办?
真是的…吼的我心里一凉,还以为大清要亡了,允许加载更多的接口数据中,一般都会有个页数和总数,到了最后一页时就会调用 adapter.loadEnd() 方法,调完之后下面的加载更多View就变成类似“–到底了–”的 endView,完全是正常流程,无任何问题嘛。
我自己在项目中用的是方案二的方式,有兴趣的可看完整代码。
源码

RecyclerView系列之加载更多相关推荐

  1. Android recyclerview上拉加载更多

    记录一下视频列表(recyclerview)上拉显示一个"加载更多"的item,然后加载数据. 效果图: 实现思路: 1.写两个item布局,一个用来展示数据,一个用来展示加载中也 ...

  2. BaseRecyclerViewAdapterHelper源码解读(四) 上拉加载更多

    上拉加载 上拉加载无需监听滑动事件,可自定义加载布局,显示异常提示,自定义异常提示. 此篇文章为BaseRecyclerViewAdapterHelper源码解读第四篇,开源库地址,如果没有看过之前3 ...

  3. android 刷新某条数据_Android 支持刷新、加载更多、带反弹效果的RecyclerView

    点击上方"Android技术杂货铺",选择"标星" 干货文章,第一时间送达! 开篇 当前市面上很多支持刷新.加载更多RecyclerView开源库,为何我这里还 ...

  4. 采用SwipeFreshLayout+Recyclerview实现下拉刷新和上拉加载更多以及CoordinatorLayout的引入

    之前都是采用PullToRefresh进行下拉刷新和下拉加载,现在采用谷歌自己的控件SwipeFreshLayout,配合Recyclerview来实现这一效果.使用SwipeRefreshLayou ...

  5. recyclerview的数据刷新(下拉刷新和自动加载更多)以及添加提示语(例如:“数据已加载完毕”)

    下拉加载更多的核心是SwipeRefreshLayout搭配Recyclerview进行使用.布局为 <android.support.v4.widget.SwipeRefreshLayout ...

  6. Android RecyclerView封装下拉刷新与上拉加载更多

    1 scanlistlibrary 基础组件说明(基于 RecyclerView的封装) 基本数据列表(支持下拉刷新与上拉加载更多) 九宫格数据显示封装(支持下拉刷新与上拉加载更多) 瀑布流数据显示封 ...

  7. Android使用RecyclerView实现上拉加载更多,下拉刷新,分组显示

    项目地址:点击打开链接(https://github.com/MrGaoGang/luckly_recyclerview) 使用RecyclerView封装headerview,footerView, ...

  8. RecyclerView的基础使用 +点击添加列表数据 +下拉刷新、上拉加载更多

    一.RecyclerView的基础使用. 第一步:添加recyclerview控件. 第二步:创建布局文件(xml) + 单独的类控制布局里面的控件(MyViewHolder). 第三步:创建一个适配 ...

  9. 使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多

    原文地址: http://blog.csdn.net/leoleohan/article/details/50989549/ 一.概述 我们公司目前开发的所有Android APP都是遵循iOS风格设 ...

最新文章

  1. (C++)1011 World Cup Betting
  2. boost::math::arcsine用法的测试程序
  3. 为什么TCP连接要三次握手?
  4. mac中的放置java类库扩展的位置
  5. CMapStringToPtr::SetAt\CAsyncSocket
  6. sql中 in , not in , exists , not exists效率分析
  7. InfoPath中的Rich Text Box中如何加“回车”
  8. 微云存照片会变模糊吗_手机自带微云台防抖,VivoX50系列不一般
  9. 字符串 读取西门子_【必学技能】自己动手——基于C#实现手机APP远程访问西门子PLC...
  10. linux zfs功能,linux – 有没有办法一次设置多个ZFS文件系统属性?
  11. 用cmd来向mysql导入sql文件
  12. 新编16 32位微型计算机答案,新编1632位微型计算机原理及应用.习题解答.ppt
  13. 涂鸦赞助的500个开发套件,先到先得
  14. IOS应用版本号设置
  15. 外贸商城建站程序, sylius和magento哪一个更适合企业二次开发
  16. Maven:解决IDEA无法下载源码
  17. 视频处理指定颜色的提取
  18. PS原生支持苹果M1Mac,速度提升50%
  19. 格式化时间戳,时间加一天
  20. python2(基本)

热门文章

  1. 典型MOSFET制造工艺流程示意图
  2. 《现代通信原理》学习(一)绪论
  3. html转docx文档
  4. mobile eye:计算机视觉、可穿戴计算 和未来的交通工具
  5. python selenium下载图片_Python Selenium Web自动化上传/下载文件图文详解
  6. readbook:自己设计mvc框架,java类似struts2的实现
  7. 《C语言及程序设计》实践参考——简单循环的流程图
  8. 备考 CPA 的,有哪些经验
  9. iphone 开发之过滤html标签
  10. 设置windows下的快捷键,例如新建文本,新建word,新建excel