Android RecyclerView自动翻页方案

作者:MrTrying

其实已经有很多上拉加载更多、或者滑动到底自动加载的自定义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();
        }
    }

这里添加了一个allowboolean变量和totalCounttotalCount用于记录当前adapter中的数据总数,如果totalCountadapter.getItemCount()不相等,说明数据发生变化,可以再继续下一次加载。

这种逻辑方式基于请求有数据返回的基础之上,如果出现那种请一次有数据更新,再一次没有,还需要在继续请求的情况是做不到的,如果有这种比较任性的需求,还是自己控制这个是否可以继续加载的标识位比较靠谱

随后,产品又招上我了,说:“为什么这个页面自动加载的这么慢,其他页面没有问题?”。这是一个网格列表,当快速滑动都底部时,等待了2s左右的时间才刷新出来下一页的数据,这TMD就有点诡异了。

一查发现,RecyclerView快速滑动到底之后,并没有马上请求下一页的数据。RecyclerView在设置为GridLayoutMannager时,RecyclerView快速滑动后继续惯性滑动到底时,OnScrollListeneronScrollStateChanged()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();
    }
}

如果有大佬有更好的解决方案,还请指点

你可能感兴趣的:(Android RecyclerView自动翻页方案)