手把手教學(xué),實(shí)現(xiàn)RecyclerView 自動(dòng)加載更多

說一下寫這個(gè)文章的原因

1.熟悉RecyclerView
2.自動(dòng)加載更多,作為每個(gè)應(yīng)用開發(fā)過程當(dāng)中幾乎必不可少的功能,了解如何實(shí)現(xiàn),然后做一些封裝,很有必要

分析

我們先來分析一下,什么時(shí)候需要用到加載更多的功能?自然是用戶向上翻動(dòng)列表,快要到所有列表項(xiàng)的底部,或則已經(jīng)到底部的時(shí)候需要加載更多的數(shù)據(jù),用于展示給用戶(為了更好的用戶體驗(yàn),肯定是用戶沒有到最低端的時(shí)候就自動(dòng)加載更多啦)。那么問題來了,怎么樣能知道快要到底部,或者已經(jīng)到底部了?翻查Android 文檔

  void addOnScrollListener (RecyclerView.OnScrollListener  listener) 

這個(gè)方法可以監(jiān)聽RecyclerView 的滾動(dòng)。哈哈距離解決問題更近一步了。我們看看OnScrollListener 里面抽象方法的參數(shù)。

void

onScrollStateChanged(RecyclerView recyclerView, int newState)
Callback method to be invoked when RecyclerView's scroll state changes.

void

onScrolled(RecyclerView recyclerView, int dx, int dy)
Callback method to be invoked when the RecyclerView has been scrolled.

這沒有辦法獲得RecyclerView 中最下面的元素是哪一個(gè)啊。要是每次滾動(dòng)的時(shí)候,我們就能拿到RecyclerView
所顯示的Item中最下面的那個(gè)的位置就好了。不急我們看看LinearLayoutManager的文檔

int findLastVisibleItemPosition ()
Returns the adapter position of the last visible view. 
This position does not include adapter changes that were dispatched after the last layout pass.
Note that, this value is not affected by layout orientation or item order traversal. (setReverseLayout(boolean) 
Views are sorted by their positions in the adapter, not in the layout.
If RecyclerView has item decorators, they will be considered in calculations as well.
LayoutManager may pre-cache some views that are not necessarily visible. Those views are ignored in this method.

這個(gè)方法就是找到最后一個(gè)可見Item的位置。太好了,只要每次滾動(dòng)的時(shí)候監(jiān)聽一下最后一個(gè)可見的Item 的位置,我們就可以決定是否架子啊更多了。
感覺我們已經(jīng)準(zhǔn)備的差不多了。
那我們開始吧

代碼設(shè)計(jì)

根據(jù)上面的思路,我們?cè)O(shè)計(jì)一下代碼。
1.當(dāng)需要加載更多的時(shí)候,肯定需要回調(diào)啊。那好我們先定義一個(gè)回調(diào)的接口:

  interface OnLoadingMore {    void onLoadMore();}

2.如果正在加載的時(shí)候,用戶又上下的滑動(dòng),再觸發(fā)了加載更多,而上次的觸發(fā)的加載還沒有結(jié)束,怎么辦?定義一個(gè)變量 ,如果正在加載了,還沒有完成,不能再次加載。

private boolean isLoading;

3.為了提高用戶的體驗(yàn),我們應(yīng)該設(shè)置一個(gè)用戶滑到提前多少個(gè)Item的時(shí)候觸發(fā)加載更多的變量。

private int visibleThreshold = 5;

4.RecyclerView 應(yīng)該有兩種類型的(暫且這么分吧,實(shí)際開發(fā)中可能會(huì)超過)。

@Override public int getItemViewType(int position) {
       if (position == getItemCount() - 1) {
             return TYPE_LOAD_MORE; 
          } else {
               return TYPE_NORMAL; 
        }
 }

感覺已經(jīng)準(zhǔn)備的差不多了,可以開始了

我們選擇在BaseAdapter 中實(shí)現(xiàn)自動(dòng)加載更多的功能,可以方便子類使用。
貼代碼啦:


public class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
    List<T> dataSet = new ArrayList<>();
    private final int TYPE_LOAD_MORE = 100;
    private final int TYPE_NORMAL = 101;

    private boolean isLoading;
    private int visibleThreshold = 5;
    OnLoadingMore loadingMore;
private boolean canLoadMore = true;

    public BaseAdapter(RecyclerView recyclerView) {
          //傳入一個(gè)RecyclerView 下面就是監(jiān)聽滾動(dòng)啦
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                int itemCount = layoutManager.getItemCount();
                int lastPosition = layoutManager.findLastVisibleItemPosition();
                Log.i("lastPosition --> ", lastPosition + "");
                Log.i("itemCount  --> ", itemCount + " ");
                //如果當(dāng)前不是正在加載更多,并且到了該加載更多的位置,加載更多。
                if (!isLoading && (lastPosition >= (itemCount - visibleThreshold))) {
                    if (canLoadMore&&loadingMore != null) {
                        isLoading = true;
                        loadingMore.onLoadMore();
                    }
                }
            }
        });
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;

        if (viewType == TYPE_LOAD_MORE) {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_load_more, parent, false);
            ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.pb_loading);
            progressBar.setInterpolator(new AccelerateInterpolator(2));
            progressBar.setIndeterminate(true);
        } else {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_normal, parent, false);
        }
        return new BaseViewHolder<>(view);
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        if (getItemViewType(position) == TYPE_LOAD_MORE) {
                View itemView=holder.itemView;
                  //判定是不是可以加載更多或則正在加載  
                   if (canLoadMore && isLoading) {
                if (itemView.getVisibility() != View.VISIBLE) {
                    itemView.setVisibility(View.VISIBLE);
                }
            } else if (itemView.getVisibility() == View.VISIBLE) {
                itemView.setVisibility(View.GONE);
            }
        } else {
            TextView textView = (TextView) holder.itemView;
            textView.setText(String.valueOf(position));
        }
    }


    @Override
    public int getItemCount() {
        return dataSet.size() + 1;
    }
    //分成兩種類型 1.普通的item 2.底部的Progress Bar
    @Override
    public int getItemViewType(int position) {
        if (position == getItemCount() - 1) {
            return TYPE_LOAD_MORE;
        } else {
            return TYPE_NORMAL;
        }
    }

    public void setLoadingMore(OnLoadingMore loadingMore) {
        this.loadingMore = loadingMore;
    }

    public void setLoading(boolean loading) {
        isLoading = loading;
    }

    public void addData(T t) {
        dataSet.add(t);
        notifyDataSetChanged();
    }
public void setCanLoadMore(boolean canLoadMore) {    this.canLoadMore = canLoadMore;}
  //回調(diào)接口
    interface OnLoadingMore {
        void onLoadMore();
    }

}

對(duì)應(yīng)的布局文件:
1.item_load_more: 很簡單的一個(gè)ProgressBar 加上 一個(gè)TextView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ProgressBar
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:id="@+id/pb_loading"
        android:layout_toLeftOf="@+id/tv_msg" />

    <TextView
        android:id="@+id/tv_msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="正在加載" />
</RelativeLayout>

2.item_normal:只是一個(gè)TextView

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:textColor="#000">
</TextView>

效果截圖:


截圖
截圖

哈哈還是挺簡單的吧。有問題的可以回復(fù)我。

完整的代碼請(qǐng)移步github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,029評(píng)論 25 709
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 14,143評(píng)論 1 92
  • 在IntelliJ IDEA 15中使用Maven時(shí),IDEA將默認(rèn)的編譯版本、源碼版本設(shè)置為jdk5。編譯項(xiàng)目的...
    天外之石閱讀 956評(píng)論 0 0
  • 我知道,就算我真得被這個(gè)男性主宰的世界拋棄,你依舊會(huì)在,依舊會(huì)捧著我的臉,含著淚說愛我…… 就像,我愛你一樣。 所...
    天官閱讀 239評(píng)論 0 0

友情鏈接更多精彩內(nèi)容