RecyclerView系列之加载更多

article/2025/9/18 2:35:46

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

grid
先说下之前的,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 状态;
至此就已经实现了加载更多功能了。
list1

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

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,完全是正常流程,无任何问题嘛。
我自己在项目中用的是方案二的方式,有兴趣的可看完整代码。
源码


http://chatgpt.dhexx.cn/article/NtblGdTL.shtml

相关文章

js页面中实现加载更多功能

分页-如何实现加载更多功能&#xff0c;目前的在很多网站上使用的加载更多功能中&#xff0c;使用最多的是iscroll.js实现的上拉加载更多、下拉刷新功能。但是iscroll.js本身并没有集成加载更多的功能&#xff0c;需要进行自行扩展。 最简单的就是给一个加载更多的按钮&#xf…

微信小程序之加载更多(分页加载)实例

1.业务需求&#xff1a;列表滚动到底部时&#xff0c;继续往上拉&#xff0c;加载更多内容 2.必传参数&#xff1a; &#xff08;1&#xff09;page: 1 //第几次加载 &#xff08;2&#xff09;limit: 5//每次加载的显示数据条数 &#xff08;3&#xff09;total: null //需要返…

自己收藏整理的一些操作系统资源

在CSDN混迹这么多年 感觉在技术宽度和广度都深不可测的C站 Windows方面的技术相对较少一些 今天&#xff0c;借着寻找C站宝藏的活动 介绍一些C站宝藏的 Windows相关资源技术专栏 附带一下猎奇操作系统的资源~~~ 【操作系统资源&#xff1a;吐血整理&#xff0c;建议收藏&a…

写代码爬取了某 Hub 资源,只为撸这个鉴黄平台!

黄色已经是我们所不容然而却防不胜防的&#xff0c;尤其是对于做内容的工具和平台&#xff0c;所以花了30分钟搭建了一个鉴黄平台&#xff0c;分享给大家。 &#xfeff; 数据准备 &#xfeff; 找了 N 多资源都不能解决问题&#xff0c;于是怒爬某 Bub资料&#xff0c;备用…

QGC(QGroundControl)地面站手把手教你改——高德地图的添加和瓦片地图在线资源

如何添加高德地图和瓦片地图在线资源 1. 演示效果2. 代码添加3. 瓦片地图在线资源3.1 高德地图3.2 天地图3.3 其它地图源相关链接 所有的热爱都要不遗余力&#xff0c;真正喜欢它便给它更高的优先级&#xff0c;和更多的时间吧&#xff01; 关于QGC地面站其它文章请点击这里: …

系统硬件资源测算

上一篇写到了架构在规划时&#xff0c;应该做哪些&#xff1f;当项目启动后&#xff0c;资源的需求就会提上议程&#xff0c;包括人力资源、项目所需的软件资源、硬件资源以及其他资源。而今天想探讨的是很少被触及的硬件资源。因为硬件资源的规划往往都是经验值的积累&#xf…

在线学习Java的资源网站

CodeGym&#xff08;https://codegym.cc/&#xff09;&#xff1a;一个在线Java编程课程&#xff0c;80%的内容是练习&#xff0c;适合一窍不通的入门者。 CodeAcademy&#xff08;https://www.codecademy.com/&#xff09;&#xff1a;该课程注重的是在找工作时非常有用的技术…

RTSP在线视频流资源地址

在线视频流地址&#xff1a; rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov 真机显示界面: 模拟器显示界面: 学好一门语言&#xff0c;动手去练&#xff0c;半天上手&#xff0c;一星期炉火纯青。—— 专攻无人车的学长

在线地图资源

一、ARCGIS在线地图资源 1&#xff0c;全球服务地址目录&#xff1a; http://services.arcgisonline.com/arcgis/rest/services 影像&#xff1a; http://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer?fjsapi 电子地图&#xff1a; ht…

【ArcGIS微课1000例】0025:ArcGIS Online当前未连接到在线资源终极解决办法

ArcGIS Online在线资源列表: World Imagery: 底图服务: 中国地图彩色版: 打开ArcGIS时,系统托盘提示“ArcGIS Online当前未连接到在线资源”,如下图所示,如果无法连接到ArcGIS Online,则就无法添加在线资源,如World Imagery等。 关于该问题,网上有多种解决办法,然而…

赶紧收藏3个免费在线资源齐全的网站

非搜 是一个综合搜索网站&#xff0c;能同时获得多个网站上的搜索结果展示&#xff0c;在APP上用户也能自己添加网站&#xff0c;除了看所有影视&#xff0c;还有小说&#xff0c;漫画&#xff0c;招聘&#xff0c;搜索&#xff0c;等搜索类型。 优点&#xff1a;不光包含影视…

arcgis当前未连接到在线资源

显示 arcgis当前未连接到在线资源时图标会打叉&#xff0c;一般按步骤破解的都没问题&#xff0c;这种情况应该是突然出现的&#xff0c;具体原因应该是你下了什么病毒软件或者操作损坏了你的网卡配置&#xff0c;这种缺陷不至于来连不了网&#xff0c;但访问界面可能有异常&am…

8个压箱底的资源网站,一个顶十个,再也不用到处找资源了

分享8个压箱底的资源网站&#xff0c;个个都让人相见恨晚&#xff0c;免费且资源丰富&#xff0c;一个顶几十个&#xff0c;有了它们就有了用不完的资源&#xff01; 1、电子书资源&#xff1a;Libgen 一个超好用的电子书搜索下载网站&#xff0c;里面的资源非常丰富&#xf…

微信排行榜主域和子域的操作

在排行榜列表UI加一个 2D精灵&#xff08;最下方&#xff09;初始化子域舞台 在初始化微信平台的时候调用 3.设置子域属性&#xff08;childCanvas 是第一步创建的2D精灵&#xff09; 在当前类的构造或者初始化调用 打开或者关闭排行榜 向子域发送消息 数据上报的key要和 子域获…

原创谷歌站站群泛目录程序

原创谷歌站群系统&#xff0c;市面仅此一家&#xff0c;请尊重知识产权&#xff0c;盗版者&#xff0c;二次盗卖者必将追究 1、系统采用静态缓冲的形式&#xff0c;亲和搜索引擎&#xff0c;无需数据库&#xff0c;无后台 2、自定义关键词、自定义文章内容&#xff0c;无需采…

discuz 二级域名设置

Discuz! X1.5 提供了对 门户、广场&#xff08;论坛&#xff09;、群组、家园的二级域名绑定功能&#xff01;但是如何正确的设置和正确的绑定二级域名呢&#xff1f;下面我就给大家带来这方面的详细 解释和设置过程&#xff01; 我的设置方法不用通过 FTP 编辑 config_g…

域内环境搭建

前言&#xff1a; 分享学习心得&#xff0c;实操笔记记录 搭建条件&#xff1a; 一台windows server 2012 的机器 任意多台不限版本windows机器 当前条件&#xff1a; windows server 2012 windows10 x64 windows 7 x64 win 2012扮演域控&#xff0c;其他两台扮演域内机器…

公域 vs 私域:哪个更优?

公域与私域是当下互联网营销中的热门话题&#xff0c;两者在营销手段和市场开发中各有优劣。公域指的是社交媒体平台、搜索引擎、电商平台等公共平台&#xff0c;而私域则是指企业自己拥有的客户资源、社群和应用等。下面我们将深入探讨公域与私域的区别和优劣。 一、公域的优…

域内文件服务器访问

五、域内文件服务器访问 克隆win2016纯净命名为文件管理器→配置网络环境&#xff08;指向域控的DNS&#xff09;→在文件管理器中c盘新建一个文件夹命名为“share”→添加角色与功能→选择服务器角色&#xff08;文件与储存服务→文件服务器&#xff09;→安装 将文件服务器添…

批量修改域账号属性

老板突然要求整理域账号的属性信息&#xff0c;还要求尽快搞定&#xff0c;几百个账号不是累死人吗&#xff1f; 无奈只能寻求使用脚本的办法了&#xff0c;经过长时间的百度google研究&#xff0c;终于搞定了&#xff0c;分享给大家。 如果只是需要修改一个属性的值&#x…