前言:上一篇文章自己動手寫RecyclerView的下拉重新整理寫過之後大家真是各種批評呀!耦合度高、考慮的情況單一什麼的.....在這裡說明一下,為了能夠讓更大家清楚的瞭解RecyclerView下拉重新整理的這種原理,所以程式碼的耦合度就高了一些!本篇文章將會為大家講解一下怎樣實現RecyclerView的上拉載入,為了講明白原理,文中程式碼的依然會緊耦合。
如果你閱讀過自己動手寫RecyclerView的下拉重新整理這篇文章,那麼你也一定知道了怎樣在RecyclerView中顯示不同的佈局了,本篇文章講的主要內容有以下兩點
- 為RecyclerView新增FooterView作為載入更多時顯示的檢視。
- 監聽RecyclerView的滑動狀態,根據不同的狀態來改變FooterView的顯示狀態。
為RecyclerView新增載入更多時的檢視
上篇講過的知識這裡就不再講了,首先看下FooterView的佈局檔案,如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loading_view"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_60"
android:gravity="center"
android:orientation="vertical">
<ViewStub
android:id="@+id/loading_viewstub"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_60"
android:layout="@layout/layout_recyclerview_footer_loading" />
<ViewStub
android:id="@+id/end_viewstub"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_60"
android:layout="@layout/layout_recyclerview_footer_end" />
</LinearLayout>
複製程式碼
這裡為了節省記憶體資源,用了ViewStub,ViewStub是一個輕量級的View,它是一個看不見的,不佔佈局位置,佔用資源非常小的控制元件。使用ViewStub可以方便的在執行時控制展示那個佈局。
在Adapter中載入FooterView
在RecyclerView中載入不同的佈局的方法,上篇已經講解過了,這裡就直接看關鍵的程式碼
@Override
public int getItemViewType(int position) {
if (isFooter(position)) {
return FOOTER_VIEW_TYPE;
}
return super.getItemViewType(position);
}
public boolean isFooter(int position) {
int lastPosition = getItemCount() - getFooterViewCount();
return getFooterViewCount() > 0 && position >= lastPosition;
}
複製程式碼
上面程式碼中isFooter方法就是判斷在position這個位置的檢視是否是FooterView。
控制FooterView的顯示隱藏
文章上部分已經講了怎樣在RecyclerView中新增FooterView,下面就重點將下FooterView在什麼時候隱藏和顯示。我們肯定知道RecyclerView在正常的狀態下FooterView也就是底部重新整理的檢視是不可見的,在我們下滑到RecyclerView的最後一個條目時才顯示底部重新整理的檢視,同時,在RecyclerView沒有將螢幕鋪滿時不會顯示底部重新整理的檢視。把這句話整理一下就是下面三點
- RecyclerView在正常狀態下,底部重新整理的檢視不可見。
- 在滑動RecyclerView時,最後一個可見的item大於或等於RecyclerView總的條目數時,顯示底部重新整理的檢視。
- 當RecyclerView的條目數未佔滿螢幕時,不會顯示底部重新整理檢視。
好了,知道了什麼時候顯示和隱藏底部重新整理的檢視,下面面臨的問題就是我們怎樣獲取RecyclerView的最後一個可見的item的position?我們知道RecyclerView總的條目數可以利用LayoutManager的getItemCount方法獲取,那麼LayoutManager中是否有獲取最後一個可見的item的position的方法呢!答案是肯定的,我們可以通過LayoutManager中的方法獲取最後一個可見的item的position,但是LayoutManager又有LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager,怎樣分別在這三個LayoutManager中獲取最後一個可見的item的position呢,答案在以下程式碼中
LayoutManager layoutManager = getLayoutManager();//獲取LayoutManager
if (layoutManagerType == null) {
if (layoutManager instanceof LinearLayoutManager) {
layoutManagerType = LayoutManagerType.LinearLayout;
} else if (layoutManager instanceof GridLayoutManager) {
layoutManagerType = LayoutManagerType.GridLayout;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
layoutManagerType = LayoutManagerType.StaggeredGridLayout;
} else {
throw new RuntimeException("LayoutManager不符合規範!");
}
}
switch (layoutManagerType) {
case LinearLayout:
mLastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
break;
case GridLayout:
mLastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
break;
case StaggeredGridLayout:
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
if (lastPositions == null) {
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
}
mLastVisibleItemPosition = findMax(lastPositions);
break;
}
複製程式碼
看過程式碼之後,可以發現這裡比較特殊的是StaggeredGridLayoutManager,由於它的特殊性,可能導致LastVisibleItemPositions有多個,這裡的處理辦法是將LastVisibleItemPositions放進陣列中,然後取出陣列中最大的數作為LastVisibleItemPosition。因為LastVisibleItemPosition根據滑動的位置而變化的,所以需要把上面的程式碼放進RecyclerView的onScrolled方法中。
文章到了這裡,我們已經知道了怎樣獲取RecyclerView的LastVisibleItemPosition和總的條目數,那什麼時候來比較這兩個數值的大小呢?當然是RecyclerView停止滾動時來比較了,用過ListView的應該知道ListView有一個監聽滑動狀態的方法,同樣RecyclerView也有這個方法,就是onScrollStateChanged(int state),RecyclerView的滑動狀態分別對應RecyclerView.SCROLL_STATE_DRAGGING、RecyclerView.SCROLL_STATE_SETTLING和RecyclerView.SCROLL_STATE_IDLE三個欄位,這個三個欄位分別代表手指在螢幕上拖動、拖動後的慣性滑動和拖動之後停止滑動。講明白之後,下面看程式碼
@Override
public void onScrollStateChanged(int state) {
//手指滑動後離開螢幕的狀態
if (state == RecyclerView.SCROLL_STATE_IDLE) {
if (mLoadMoreEnabled) {
LayoutManager layoutManager = getLayoutManager();
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
//mLastVisibleItemPosition >= totalItemCount-1是因為新增了一個FooterView
//totalItemCount>visibleItemCount是限制當totalItemCount沒鋪滿螢幕時不顯示FooterView
if (visibleItemCount > 0
&& mLastVisibleItemPosition >= totalItemCount-1
&& totalItemCount>visibleItemCount
&& !isNoMore) {
mFootView.setVisibility(View.VISIBLE);//這裡顯示FootView
if (mLoadingData) {
return;
} else {
mLoadingData = true;
mILoadMoreFooter.onLoading();//方法回撥
if (mLoadMoreListener != null) {
mLoadMoreListener.onLoadMore();
}
}
}
}
}
}
複製程式碼
程式碼中一些可能比較繞的地方已經進行了註釋,這裡就不再講解了,已經知道了怎樣控制FooterView的顯示隱藏,下一步就是改變FooterView的狀態了。
改變FooterView的狀態
因為是在RecyclerView中來改變FooterView的狀態的,所以這裡需要定義一個回撥介面,如下
public interface ILoadMoreFooter {
void onReset();//正常的狀態下
void onLoading();//正在載入中的狀態
void onComplete();//已經載入完成
void LoadNoMore();//沒有更多資料
View getFooterView();//獲取FooterView
}
複製程式碼
再看下FooterView的程式碼
public class LoadMoreFooter extends RelativeLayout implements ILoadMoreFooter{
private State mState;
private View mLoadingView; //正在載入的圖示
private View mTheEndView; //載入全部的圖示
//此處省略部分程式碼
public LoadMoreFooter(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
inflate(context, R.layout.layout_recyclerview_footer, this);
onReset();
}
@Override
public void onReset() {
onComplete();
}
@Override
public void onLoading() {
setState(State.Loading);
}
@Override
public void onComplete() {
setState(State.Normal);
}
@Override
public void LoadNoMore() {
setState(State.NoMore);
}
private void setState(State state) {
if (mState == state) {
return;
}
mState = state;
switch (state) {
case Normal:
if (mLoadingView != null) {
mLoadingView.setVisibility(GONE);
}
if (mTheEndView != null) {
mTheEndView.setVisibility(GONE);
}
break;
case Loading:
if (mTheEndView != null) {
mTheEndView.setVisibility(GONE);
}
if (mLoadingView == null) {
ViewStub viewStub = findViewById(R.id.loading_viewstub);
mLoadingView= viewStub.inflate();
}
mLoadingView.setVisibility(VISIBLE);
break;
case NoMore:
if (mLoadingView != null) {
mLoadingView.setVisibility(GONE);
}
if (mTheEndView == null) {
ViewStub viewStub = findViewById(R.id.end_viewstub);
mTheEndView= viewStub.inflate();
}
mTheEndView.setVisibility(VISIBLE);
break;
}
}
@Override
public View getFooterView() {
return this;
}
public enum State {
Normal,
Loading,
NoMore,
}
}
複製程式碼
可以看到,這裡面的主要程式碼就是setState方法中的程式碼,setState方法中的程式碼也比較簡單就是控制佈局的顯示隱藏。文章到了這裡,已經將實現RecyclerView載入更多的功能中的主要的部分講解完了,下面看下效果圖
結束語
文中只是貼出了一些比較重要的程式碼,獲取完整的程式碼,點選這裡。
ps: 歷史文章中有乾貨哦!
轉載請註明出處:www.wizardev.com