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

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

// 滑动最后一个Item的时候回调onLoadMoreRequested方法

setOnLoadMoreListener(RequestLoadMoreListener);

默认第一次加载会回调onLoadMoreRequested()加载更多这个方法,如果不需要可以配置如下:

mQuickAdapter.disableLoadMoreIfNotFullPage();

回调处理代码

mQuickAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {

@Override public void onLoadMoreRequested() {

mRecyclerView.postDelayed(new Runnable() {

@Override

public void run() {

if (mCurrentCounter >= TOTAL_COUNTER) {

//数据全部加载完毕

mQuickAdapter.loadMoreEnd();

} else {

if (isErr) {

//成功获取更多数据

mQuickAdapter.addData(DataServer.getSampleData(PAGE_SIZE));

mCurrentCounter = mQuickAdapter.getData().size();

mQuickAdapter.loadMoreComplete();

} else {

//获取更多数据失败

isErr = true;

Toast.makeText(PullToRefreshUseActivity.this, R.string.network_err, Toast.LENGTH_LONG).show();

mQuickAdapter.loadMoreFail();

}

}

}

}, delayMillis);

}

}, mReyclerView);

这里可能看的不是很清楚,详情请看官方demo,https://github.com/CymChad/BaseRecyclerViewAdapterHelper/blob/d296d1fb4e7a64b9fa8a2601f3f896d3a9518be5/app/src/main/java/com/chad/baserecyclerviewadapterhelper/PullToRefreshUseActivity.java

加载完成(注意不是加载结束,而是本次数据加载结束并且还有下页数据)

mQuickAdapter.loadMoreComplete();

加载失败

mQuickAdapter.loadMoreFail();

加载结束

mQuickAdapter.loadMoreEnd();

设置监听器,开启监听上拉加载


/**

  • 设置监听RecyclerView上拉加载更多 并设置监听器

  • @param requestLoadMoreListener

  • @param recyclerView

*/

public void setOnLoadMoreListener(RequestLoadMoreListener requestLoadMoreListener,

RecyclerView recyclerView) {

openLoadMore(requestLoadMoreListener);

if (getRecyclerView() == null) {

setRecyclerView(recyclerView);

}

}

/**

  • 开启上拉加载更多

  • @param requestLoadMoreListener

*/

private void openLoadMore(RequestLoadMoreListener requestLoadMoreListener) {

this.mRequestLoadMoreListener = requestLoadMoreListener;

mNextLoadEnable = true;

mLoadMoreEnable = true;

mLoading = false;

}

设置什么时候回调?


/**

  • 设置当列表滑动到倒数第N个Item的时候(默认是1)回调onLoadMoreRequested()方法

  • @param preLoadNumber

*/

public void setPreLoadNumber(int preLoadNumber) {

if (preLoadNumber > 1) {

mPreLoadNumber = preLoadNumber;

}

}

先来说简单的,上面这个方法比较简单,属于配置型的方法.

就是设置当列表滑动到倒数第N个Item的时候(默认是1)回调onLoadMoreRequested()方法.待会儿下面会用到这个参数,先放着.

另外,这个方法可以在使用时不必调用,因为已经有默认值了.

/**

  • To bind different types of holder and solve different the bind events

  • @param holder

  • @param position

  • @see #getDefItemViewType(int)

*/

@Override

public void onBindViewHolder(K holder, int position) {

//Add up fetch logic, almost like load more, but simpler.

//这里是判断是否需要下拉加载更多

autoUpFetch(position);

//Do not move position, need to change before LoadMoreView binding

//判断是否需要进行上拉加载更多

autoLoadMore(position);

int viewType = holder.getItemViewType();

switch (viewType) {

case 0:

convert(holder, getItem(position - getHeaderLayoutCount()));

break;

case LOADING_VIEW:

mLoadMoreView.convert(holder);

break;

case HEADER_VIEW:

break;

case EMPTY_VIEW:

break;

case FOOTER_VIEW:

break;

default:

convert(holder, getItem(position - getHeaderLayoutCount()));

break;

}

}

/**

  • 根据position位置判断当前是否需要进行加载更多

  • @param position 当前onBindViewHolder()的Position

*/

private void autoLoadMore(int position) {

// 判断是否可以进行加载更多的逻辑

if (getLoadMoreViewCount() == 0) {

return;

}

//只有在当前列表的倒数mPreLoadNumber个item开始绑定数据时才进行加载更多的逻辑

if (position < getItemCount() - mPreLoadNumber) {

return;

}

//判断当前加载状态,如果不是默认状态(可能正处于 正在加载中 的状态),则不进行加载

if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {

return;

}

//设置当前状态:加载中

mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);

if (!mLoading) {

mLoading = true;

if (getRecyclerView() != null) {

getRecyclerView().post(new Runnable() {

@Override

public void run() {

//回调 让调用者去处理加载更多的逻辑

mRequestLoadMoreListener.onLoadMoreRequested();

}

});

} else {

mRequestLoadMoreListener.onLoadMoreRequested();

}

}

}

/**

  • Load more view count

  • 判断是否可以进行加载更多的逻辑

  • @return 0 or 1

*/

public int getLoadMoreViewCount() {

//参数合法性 加载更多状态

if (mRequestLoadMoreListener == null || !mLoadMoreEnable) {

return 0;

}

//可加载下一页 有无更多数据

if (!mNextLoadEnable && mLoadMoreView.isLoadEndMoreGone()) {

return 0;

}

//当前数据项个数

if (mData.size() == 0) {

return 0;

}

return 1;

}

重点来了,加载更多的主要逻辑就在这里:当在onBindViewHolder()的时候,根据当前item的position位置,然后去判断是否应该执行加载更多.

具体判断逻辑:当一个item第一次进入window界面时,会调用onBindViewHolder()去绑定数据,这个时候我们知道该position的位置,

于是我们就可以这样干:设置一个mPreLoadNumber标志位置( 当列表滑动到倒数第N个Item的时候(默认是1)回调onLoadMoreRequested()方法 ),

当onBindViewHolder()在绑定数据时的position是最后mPreLoadNumber个时,我们即进行加载更多的回调,然后让调用者去处理.

当然,在回调之前,我们需要进行一些判断,确定当前是否可以进行加载更多.

- mRequestLoadMoreListener监听器是否为null,当前是否处于可以加载更多的状态(mLoadMoreEnable标志位控制)

- 当前有无更多数据(这个由外界调用者决定)

- 当前的数据项个数是否为0,如果没有数据项,那就不必加载更多

- 是否进入倒数的那mPreLoadNumber区域

- 判断当前(mLoadMoreView 这是加载更多的View )加载状态,如果不是默认状态(可能正处于 正在加载中 的状态),则不进行加载

好吧,细心的观众可能已经发现了,上面的这种方式其实有一个缺点:当数据项个数小于1屏幕,那么最后倒数的mPreLoadNumber个肯定是可见的,既然可见那么肯定会执行该item的onBindViewHolder(),执行该方法即会判断是否需要执行加载更多,显然这时是符合条件的,于是就会出现数据未满一屏幕会自动回调onLoadMoreRequested()并且还在那里显示正在加载中.

明显,这时不符合我们的需求的.于是官方有一个解决方案.往下看.

/**

  • bind recyclerView {@link #bindToRecyclerView(RecyclerView)} before use!

  • @see #disableLoadMoreIfNotFullPage(RecyclerView)

*/

public void disableLoadMoreIfNotFullPage() {

//检查当前RecyclerView是否为null

checkNotNull();

disableLoadMoreIfNotFullPage(getRecyclerView());

}

/**

  • check if full page after {@link #setNewData(List)}, if full, it will enable load more again.

  • 不是配置项!!

  • 这个方法是用来检查是否满一屏的,所以只推荐在 {@link #setNewData(List)} 之后使用

  • 原理:先关闭 load more,检查完了再决定是否开启

  • 数据项个数未满一屏幕,则不开启load more

  • 数据项个数 > 一屏幕,则继续开启load more

  • 不是配置项!!

  • @param recyclerView your recyclerView

  • @see #setNewData(List)

*/

public void disableLoadMoreIfNotFullPage(RecyclerView recyclerView) {

// 设置加载状态为false

setEnableLoadMore(false);

if (recyclerView == null) return;

RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();

if (manager == null) return;

if (manager instanceof LinearLayoutManager) {

final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;

recyclerView.postDelayed(new Runnable() {

@Override

public void run() {

//数据项个数 > 一屏幕,则继续开启load more

if ((linearLayoutManager.findLastCompletelyVisibleItemPosition() + 1) !=

getItemCount()) {

setEnableLoadMore(true);

}

}

}, 50);

} else if (manager instanceof StaggeredGridLayoutManager) {

final StaggeredGridLayoutManager staggeredGridLayoutManager =

(StaggeredGridLayoutManager) manager;

recyclerView.postDelayed(new Runnable() {

@Override

public void run() {

//返回StaggeredGridLayoutManager布局的跨度数

final int[] positions = new int[staggeredGridLayoutManager.getSpanCount()];

//返回每一个跨度(列)的最后一个可见的item的位置 赋值到该数组里面

staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(positions);

//找出数组中最大的数(即StaggeredGridLayoutManager布局的当前可见的最下面那个item)

int pos = getTheBiggestNumber(positions) + 1;

// 数据项个数 > 一屏幕,则继续开启load more

if (pos != getItemCount()) {

setEnableLoadMore(true);

}

}

}, 50);

}

}

/**

  • 返回数组中的最大值

  • @param numbers

  • @return

*/

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;

}

/**

  • Set the enabled state of load more.

  • 设置上拉加载更多是否可用

  • @param enable True if load more is enabled, false otherwise.

*/

public void setEnableLoadMore(boolean enable) {

//之前的状态需要和现在的状态做对比

int oldLoadMoreCount = getLoadMoreViewCount();

mLoadMoreEnable = enable;

int newLoadMoreCount = getLoadMoreViewCount();

if (oldLoadMoreCount == 1) {

if (newLoadMoreCount == 0) {

//之前有 现在没有 需要移除

notifyItemRemoved(getLoadMoreViewPosition());

}

} else {

if (newLoadMoreCount == 1) {

//将加载布局插入

mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);

notifyItemInserted(getLoadMoreViewPosition());

}

}

}

这段代码我看到在开源项目的讨论区异常热门,好像很多人都遇到了使用disableLoadMoreIfNotFullPage()无效的事件.

可能是他们用错了吧,可能.disableLoadMoreIfNotFullPage()是需要在setNewData()之后调用才有效.

disableLoadMoreIfNotFullPage()里面想做的事情就是:判断是否需要load more,他判断的依据是:

查看当前屏幕内的最底部的那个item的索引是否与总的数据项个数相等.

- 如果相等,那么说明未满一屏幕,不需要开启load more

- 如果不相等,那么说明满了一屏幕,需要开启laod more

创建加载布局item 并 设置加载布局的点击事件


@Override

public K onCreateViewHolder(ViewGroup parent, int viewType) {

K baseViewHolder = null;

this.mContext = parent.getContext();

this.mLayoutInflater = LayoutInflater.from(mContext);

switch (viewType) {

case LOADING_VIEW:

baseViewHolder = getLoadingView(parent);

break;

case HEADER_VIEW:

baseViewHolder = createBaseViewHolder(mHeaderLayout);

break;

case EMPTY_VIEW:

baseViewHolder = createBaseViewHolder(mEmptyLayout);

break;

case FOOTER_VIEW:

baseViewHolder = createBaseViewHolder(mFooterLayout);

break;

default:

baseViewHolder = onCreateDefViewHolder(parent, viewType);

bindViewClickListener(baseViewHolder);

}

baseViewHolder.setAdapter(this);

return baseViewHolder;

}

private K getLoadingView(ViewGroup parent) {

//加载 加载布局

View view = getItemView(mLoadMoreView.getLayoutId(), parent);

//生成baseviewholder

K holder = createBaseViewHolder(view);

//设置加载布局的点击事件

holder.itemView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

if (mLoadMoreView.getLoadMoreStatus() == LoadMoreView.STATUS_FAIL) {

//之前是加载失败状态时 前去刷新

notifyLoadMoreToLoading();

}

if (mEnableLoadMoreEndClick && mLoadMoreView.getLoadMoreStatus() == LoadMoreView

.STATUS_END) {

//加载更多布局可以被点击 并且 之前状态是结束状态

notifyLoadMoreToLoading();

}

}

});

return holder;

}

/**

  • The notification starts the callback and loads more

  • 通知启动回调并加载更多

*/

public void notifyLoadMoreToLoading() {

//如果当前正在加载中,则不用管

if (mLoadMoreView.getLoadMoreStatus() == LoadMoreView.STATUS_LOADING) {

return;

}

//将加载更多布局的状态设置为默认状态 这样当下面刷新adapter时会回调onBindViewHolder()从而触发

//autoLoadMore()方法去判断是否需要加载更多,这时候刚好又是默认状态是可以更新的,于是就去回调onLoadMoreRequested()方法

mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);

notifyItemChanged(getLoadMoreViewPosition());

}

这里的目标是给加载更多布局设置点击事件,可以看到其实在代码里面把加载更多布局是直接设置了点击事件的,只是根据不同的状态决定是否需要执行加载更多的逻辑.只有下面2种情况需要去加载更多.

- 之前是加载失败状态时 加载布局被点击

- 之前是结束状态 并且 加载更多布局可以被点击

满足这两种情况时,就把加载布局view的状态设置成默认状态,并且刷新adapter的最后一项(即加载更多布局那一项),这样adapter会回调onBindViewHolder(),而在onBindViewHolder()又调用了autoLoadMore()方法去判断是否需要加载更多,

显然此时是符合条件的,需要刷新,于是回调onLoadMoreRequested(),并且把加载布局的状态改为STATUS_LOADING正在加载的状态,这样加载布局的样式也跟着改变了.

加载完成


注意不是加载结束,而是本次数据加载结束并且还有下页数据

/**

  • Refresh complete

  • 刷新完成时调用

*/

public void loadMoreComplete() {

if (getLoadMoreViewCount() == 0) {

return;

}

//将当前加载状态改为false 表示未在加载

mLoading = false;

//可进行下一页加载

mNextLoadEnable = true;

// 恢复加载更多布局的状态

mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);

// 告知加载更多布局被更新了,需要刷新一下

notifyItemChanged(getLoadMoreViewPosition());

}

/**

  • Gets to load more locations

  • 获取加载更多的布局的索引

  • @return

*/

public int getLoadMoreViewPosition() {

return getHeaderLayoutCount() + mData.size() + getFooterLayoutCount();

}

刷新完成之后,需要做一些善后操作,如上所示,代码注释已经很清楚了.

加载失败


/**

  • Refresh failed

  • 加载失败

*/

public void loadMoreFail() {

if (getLoadMoreViewCount() == 0) {

return;

}

//当前加载状态 切换为未在加载中

最后

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

image

image

其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

);

}

/**

  • Gets to load more locations

  • 获取加载更多的布局的索引

  • @return

*/

public int getLoadMoreViewPosition() {

return getHeaderLayoutCount() + mData.size() + getFooterLayoutCount();

}

刷新完成之后,需要做一些善后操作,如上所示,代码注释已经很清楚了.

加载失败


/**

  • Refresh failed

  • 加载失败

*/

public void loadMoreFail() {

if (getLoadMoreViewCount() == 0) {

return;

}

//当前加载状态 切换为未在加载中

最后

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

[外链图片转存中…(img-Uk7cRWl6-1713596435721)]

[外链图片转存中…(img-tJ6M3tCF-1713596435722)]

其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Y6eZ8iRu-1713596435722)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在uniapp中实现加载更多的功能可以通过使用onReachBottom事件来实现。当用户滑动页面到底部时,就会触发onReachBottom事件,我们可以在这个事件的回调函数中实现加载更多的逻辑。首先,我们需要在页面的配置文件(如.vue文件)中注册onReachBottom事件。然后在事件回调函数中执行加载更多的操作,可以通过向后端发送请求获取更多的数据,并将新数据添加到已有的数据列表中。最后,将更新后的数据重新渲染到页面上,从而实现加载更多的效果。这样,当用户滑动页面到底部时,就会自动加载更多的数据了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [【uniapp开发实战】点击加载更多,每次增加 n 条数据的实现方法](https://blog.csdn.net/Shids_/article/details/117815570)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [uniapp滚动加载 下拉刷新](https://blog.csdn.net/2303_76218115/article/details/130981650)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java微信小程序B2C商城 H5+APP源码 前后端分离](https://download.csdn.net/download/m0_55416028/88263033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值