Android RecyclerView自動翻頁方案
其實已經有很多上拉載入更多、或者滑動到底自動載入的自定義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(); } }
如果有大佬有更好的解決方案,還請指點