其实已经有很多上拉加载更多、或者滑动到底自动加载的自定义RecyclerView,这里所使用的方案是通用于RecyclerView的,目的就是为了提高代码复用
通常在app的列表中会使用分页加载数据,当用户停止滑动列表到达底部时会加载下一页数据;为了更好地用户体验,可以在列表停止滑动是会提前几个item加载下一页数据。
public static void setLoad(@NonNull RecyclerView recyclerView, final OnLoadCallback onLoadCallback) {
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int visibleLast = -1;
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(newState == SCROLL_STATE_IDLE){
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if(layoutManager == null){
return;
}
if (layoutManager instanceof LinearLayoutManager) {
visibleLast = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] lastItemArr = ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(null);
if (lastItemArr.length > 0) {
visibleLast = lastItemArr[lastItemArr.length - 1];
}
}
if (recyclerView.getAdapter() != null
&& recyclerView.getAdapter().getItemCount() - 4 <= visibleLast) {
if (onLoadCallback != null) {
onLoadCallback.onLoad();
}
}
}
}
});
if (onLoadCallback != null) {
onLoadCallback.onLoad();
}
}
这个需求代码还是比较简单的,基于这个需求,基本实现思路就是为RecyclerView设置滑动监听,在回调中处理请求下一页数据的回调。
上面的代码有个问题,实际使用中onLoadCallback可能会被重复回调
public static void setLoad(@NonNull RecyclerView recyclerView, final OnLoadCallback onLoadCallback) {
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int visibleLast = -1;
int totalCount;
boolean allow = true;
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(newState == SCROLL_STATE_IDLE){
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if(layoutManager == null){
return;
}
if (layoutManager instanceof LinearLayoutManager) {
visibleLast = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] lastItemArr = ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(null);
if (lastItemArr.length > 0) {
visibleLast = lastItemArr[lastItemArr.length - 1];
}
}
if(recyclerView.getAdapter() != null){
if(!allow){
allow = totalCount != recyclerView.getAdapter().getItemCount();
}
if (allow && recyclerView.getAdapter().getItemCount() - 4 <= visibleLast) {
if (onLoadCallback != null) {
onLoadCallback.onLoad();
}
}
}
}
}
});
if (onLoadCallback != null) {
onLoadCallback.onLoad();
}
}
这里添加了一个allow的boolean变量和totalCount,totalCount用于记录当前adapter中的数据总数,如果totalCount与adapter.getItemCount()不相等,说明数据发生变化,可以再继续下一次加载。
这种逻辑方式基于请求有数据返回的基础之上,如果出现那种请一次有数据更新,再一次没有,还需要在继续请求的情况是做不到的,如果有这种比较任性的需求,还是自己控制这个是否可以继续加载的标识位比较靠谱
随后,产品又招上我了,说:“为什么这个页面自动加载的这么慢,其他页面没有问题?”。这是一个网格列表,当快速滑动都底部时,等待了2s左右的时间才刷新出来下一页的数据,这TMD就有点诡异了。
一查发现,RecyclerView快速滑动到底之后,并没有马上请求下一页的数据。RecyclerView在设置为GridLayoutMannager时,RecyclerView快速滑动后继续惯性滑动到底时,OnScrollListener的onScrollStateChanged()从SCROLL_STATE_SETTLING状态变更到SCROLL_STATE_IDLE状态花了1.8~2.4s左右的时间(开始怀疑人生,难道是我打开方式不对?)。
没招了,只能将代码从onScrollStateChanged()放到onScrolled()里,这样一来确实有一部分的性能损失,但是这一情况太影响用户体验之只能这么办。适当的调整了一下代码的逻辑,尽量减少无效的findItem计算。
public static void setLoad(@NonNull RecyclerView recyclerView, final OnLoadCallback onLoadCallback, boolean isAuto) {
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int visibleLast = -1;
int totalCount;
boolean allow = true;
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (recyclerView.getAdapter() != null) {
if (!allow) {
allow = totalCount != recyclerView.getAdapter().getItemCount();
}
}
if (!allow) {
return;
}
RecyclerView.LayoutManager mLayoutManager = recyclerView.getLayoutManager();
if (mLayoutManager == null) {
return;
}
if (mLayoutManager instanceof LinearLayoutManager) {
visibleLast = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
} else if (mLayoutManager instanceof StaggeredGridLayoutManager) {
int[] lastItemArr = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
if (lastItemArr.length > 0) {
visibleLast = lastItemArr[lastItemArr.length - 1];
}
}
if (recyclerView.getAdapter() != null
&& recyclerView.getAdapter().getItemCount() - 4 <= visibleLast
&& allow) {
allow = false;
totalCount = recyclerView.getAdapter().getItemCount();
if (onLoadCallback != null) {
onLoadCallback.onLoad();
}
}
}
});
if (onLoadCallback != null) {
onLoadCallback.onLoad();
}
}
如果有大佬有更好的解决方案,还请指点