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