version:2.8.5
更多分享請看:http://cherylgood.cn
我們在上一章中分析了實現預加載功能的代碼,相信自己,你也可以,每個人都是創造者。
本章我將分析BaseRecyclerViewAdapterHelper 中 實現加載更多功能的代碼。
首先我們先了解幾個有關加載更多功能的方法,
第一步:打開上拉加載的開關
/**
* 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(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
} else {
if (newLoadMoreCount == 1) {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
}
}
通過上面方法打開我們的上拉加載的開關。首先我們先看下以下兩個變量的意思。
1、oldLoadMoreCount 代表在改變這個開關時我們是否處於顯示上拉加載的view的狀態,1表示處於該狀態。
2、newLoadMoreCount 代表我們當前是否可以開啓上拉加載功能,同樣,1表示可以。
這段代碼很有意思
if (oldLoadMoreCount == 1) {
if (newLoadMoreCount == 0) {
notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
我們爲什麼要做插入這段代碼呢,他的作用其實是這樣的:加入當前處於顯示加載更多view的狀態,此時你想關閉該開關,那我們第一件事要做什麼呢,當然是移除加載更多view 了,這段代碼的作用就是這個。
反過來,我們現在要開啓上拉加載。走的是這段代碼
else {
if (newLoadMoreCount == 1) {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
}
因爲們的的loadMoreView一直是處於最底部的一個view,所以我們通過調用
notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount()); 告訴recycerlView將loadViewMore顯示出來。 當我們同過上拉加載加載新的數據完成後,我們需要告訴BaseQuickAdapter你可以恢復正常狀態了,此時我們將用到以下方法: //加載完成第一個if是防止我們錯誤的調用該方法。可以看到,方法內部幫我們調用了更新數據源的方法。而且是局部更新。
/**
* Refresh complete
*/
public void loadMoreComplete() {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
我們看到這麼一句恢復我們的loadMoreView爲默認值,我們可以跟進去看下一下
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
他內部是重置了loadMoreStatus這個字段
public void setLoadMoreStatus(int loadMoreStatus) {
this.mLoadMoreStatus = loadMoreStatus;
}
而這個字段是在什麼時候用到呢,LoadMoreView的代碼很少,可以看到
public void convert(BaseViewHolder holder) {
switch (mLoadMoreStatus) {
case STATUS_LOADING:
visibleLoading(holder, true);
visibleLoadFail(holder, false);
visibleLoadEnd(holder, false);
break;
case STATUS_FAIL:
visibleLoading(holder, false);
visibleLoadFail(holder, true);
visibleLoadEnd(holder, false);
break;
case STATUS_END:
visibleLoading(holder, false);
visibleLoadFail(holder, false);
visibleLoadEnd(holder, true);
break;
case STATUS_DEFAULT:
visibleLoading(holder, false);
visibleLoadFail(holder, false);
visibleLoadEnd(holder, false);
break;
}
}
他是在一個convert方法中根據mLoadMoreStatus來改變loadMoreView的顯示和隱藏,convert方法大家應該很熟悉,參數holder其實就是我們的loadMoreView本身,那麼loadMoreView.convert在哪被調用呢。
其實是在我們綁定數據時,如果判斷當前viewholder時loadMore類型,就會調用。
@Override
public void onBindViewHolder(K holder, int positions) {
Log.d(TAG,"#test onBindViewHolder");
int viewType = holder.getItemViewType();
switch (viewType) {
case 0:
convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
break;
case LOADING_VIEW:
mLoadMoreView.convert(holder);
break;
case HEADER_VIEW:
break;
case EMPTY_VIEW:
break;
case FOOTER_VIEW:
break;
default:
convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
break;
}
}
很好理解,我們的loadMoreView是一直存在的。作爲我們recyclerView的最後一個item,當加載到最後一個item的時候,他就調用loadView的convert方法,方法內部根據我們當前是否應該顯示loadView來做相應的操作。這樣我們就理解了loadMore的隱藏和顯示的邏輯了。後面還有兩個方法也很好理解,請看。
加載失敗調用,可能你有需求在加載失敗後要顯示一個加載失敗的view提示用戶,而不是直接關閉loadMoreView。此時你可以調用該方法。
/**
* Refresh failed
*/
public void loadMoreFail() {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL);
notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
/**
* Refresh end, no more data
*
* @param gone if true gone the load more view
*/
public void loadMoreEnd(boolean gone) {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mNextLoadEnable = false;
mLoadMoreView.setLoadMoreEndGone(gone);
if (gone) {
notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
} else {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END);
notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
}
之後就到我們關心的回調部分了。首先我們需要設置我們的回調監聽器
public void setOnLoadMoreListener(RequestLoadMoreListener requestLoadMoreListener) {
this.mRequestLoadMoreListener = requestLoadMoreListener;
mNextLoadEnable = true;
mLoadMoreEnable = true;
mLoading = false;
}
在設置監聽器的時候,代碼也幫我們做了一個字段的賦值操作。默認開啓上拉加載,mLoading是表示當前是否處於上拉加載中。
接下來你可能要問,那這個mrequestLoadMoreListener在什麼時候被調用呢,其實這個也設計的比較好,上一章我們分析了預加載功能,其實就在上次介紹的代碼中。
private void autoLoadMore(int position) {
//只有開啓了上拉加載且loadMoreView沒有gone且data.size>0 時返回1
if (getLoadMoreViewCount() == 0) {
return;
}
if (position < getItemCount() - mAutoLoadMoreSize) {
return;
}
if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {
return;
}
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);
if (!mLoading) {
mLoading = true;
mRequestLoadMoreListener.onLoadMoreRequested();
}
}
如果你想關閉預加載,當時是mAutoLoadMoreSize =0 ,此時要調用最後一句代碼,條件就變成了
position<getItemCount() ==false,而其成立的條件是position==getItemCount 也就是加載到最後一個item時。
思路大概是這樣的:
在關閉預加載功能時:如果加載到最後一個item,首先會調用getItemViewType詢問即將加載的viewholder是什麼類型。我們前面說過了,loadMoreView是一直作爲最後一個viewHolder存在的。此時如果符合顯示loadMoreView條件,那麼就設置loadMoreView的狀態,調用我們的函數。在執行到onBindViewHolder生命週期方法時,會根據我們設置的值顯示或者隱藏loadMoreView視圖,也就是說,onLoadMoreRequested方法的回調在顯示loadMoreView之前就被調用了。時間是很短的,用戶基本察覺不出來。
本次分析就到這裏,接下來會繼續分析其餘的代碼。如果有需要想一起分析哪部份代碼,也可以直接留言,我會調整順序。