自定義RecyclerView實現下拉刷新,加載更多

RecyclerView出來的時間已經不短了,現在估計大部分的列表類的需求實現首選肯定是RecyclerView,基本上可以跟ListView說再見了。那麼問題來了,一般情況下一個列表頁面都會有下拉刷新和加載更多功能,RecyclerView本身並沒有下拉刷新和加載更多功能,當然現在已經有很多優秀的開源的支持下拉刷新,加載更多功能的三方RecyclerView,可以直接拿過來用。但是。。。有時候光會用是不夠的,還需要知道它們是這麼實現的,實現的原理是什麼。下面就來介紹一下RecyclerView下拉刷新,加載更多功能的實現套路。

要實現的效果

這裏寫圖片描述

一、下拉刷新view和加載更多的view放在哪?

要達到上面的效果首先要考慮的是這個頂部下拉的刷新的view和底部加載更多的view放在什麼地方合適,答案就是自定義一個WrapAdapter適配器,通過包裝Adapter來提供header和footer。因爲RecyclerView的Adapter是支持顯示多種不同類型的view的,只需要重寫RecyclerView.Adapter的 getItemViewType(int position)方法,根據不同位置返回不同類型即可。可以利用這個特性把第0個位置和最後一個位置預留出來,固定把第0個item存放下拉刷新的view,把最後一個位置存放加載更多的view。
具體代碼如下:

/**
 *  實現顯示頭部和尾部item的adapter,把頭部尾部的事情交給這個adapter來做,其他的交給子adapter
 */
public class MyWrapAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public ArrayList<View> headerViews=new ArrayList<>();
    public ArrayList<View> footViews=new ArrayList<>();
    public RecyclerView.Adapter adapter;

    public MyWrapAdapter(RecyclerView.Adapter adapter, ArrayList<View> headerViews, ArrayList footViews){
        this.adapter=adapter;
        this.headerViews=headerViews;
        this.footViews=footViews;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(viewType== RecyclerView.INVALID_TYPE){
            //頭部item
            return new RecyclerView.ViewHolder(headerViews.get(0)){};
        }else if(viewType== (RecyclerView.INVALID_TYPE-1)){
            //尾部item
            return new RecyclerView.ViewHolder(footViews.get(0)){};
        }
        return adapter.onCreateViewHolder(parent,viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if(position>=0&&position<headerViews.size()){
            return;
        }

        if(adapter!=null){
            int p=position-headerViews.size();
            if(p<adapter.getItemCount()){
                adapter.onBindViewHolder(holder,p);
            }
        }
    }

    @Override
    public int getItemViewType(int position) {
        if(position>=0&&position<headerViews.size()){
            //如果是頭部則返回一個不可用的標識,表示這是頭部item
            return RecyclerView.INVALID_TYPE;
        }

        if(adapter!=null){
            int p=position-headerViews.size();
            if(p<adapter.getItemCount()){
                return adapter.getItemViewType(p);
            }
        }

        return RecyclerView.INVALID_TYPE-1;//默認返回表示是尾部的item
    }

    @Override
    public int getItemCount() {
        return getCount();
    }

    public int getCount(){
        int count=headerViews.size()+footViews.size();
        if(adapter!=null){
            count+=adapter.getItemCount();
        }
        return count;
    }

}

二、下拉刷新的實現

整體實現下拉刷新的思路是,當把下拉刷新view添加到adapter中的第0個item的時候,高度是0,也就看不見下拉刷新的view。當我們下拉刷新的時候根據滑動的值來設置下拉刷新view的高度,這樣下拉刷新的view就慢慢的顯示出來了。但是現在的效果是,先看到的是頂部,下拉刷新view是從頂部開始一點一點顯示的。這樣的效果當然可以,但是我們需要的效果是下拉刷新的view從底部開始一點點顯示出來,做如下調整,我們一開始讓下拉刷新的view的高度就是自身高,讓它距離頂部的距離是負的自身高度,這樣正好就看不見了。並且把下拉刷新view的Gravity設置爲Gravity.BOTTOM,這樣當我們滑動的時候下拉刷新的view就會從底部開始一點一點顯示出來。

現在思路是有了,但是實現的時候,需要解決下面幾個問題:
1、下拉刷新只有在滑動到頂部的時候,纔會觸發下拉刷新,那麼RecyclerView如何判斷滑動到了頂部?
研究了幾個開源的下拉刷新RecyclerView,發現每個實現的方法都不太一樣,踩了幾個坑後。決定用
ViewCompat.canScrollVertically()這個方法來實現,這個方法能夠檢測出RecyclerView是否滑動到了頂部或底部。
2、如何獲得滑動的值
這個老套路了,通過重寫RecyclerView的onTouchEvent來計算獲得。還有一共方式可以獲得滑動的值,重寫LayoutManager的scrollVerticallyBy方法這個方法的參數值是LayoutManager幫我們計算好的滑動距離,但是用這種方式還得自己包一個LayoutManager,所以不考慮,還是老套路吧。。。

三、加載更多的實現

加載更多就比較好實現了,核心是如何判斷recyclerview滾動到了底部。
一種思路給RecyclerView添加addOnScrollListener監聽,重寫onScrollStateChanged方法,在這裏面判斷最後一個可見的view是否是最後一個item即可。
判斷的代碼如下:

this.addOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (isRefresh) {
                    return;
                }
                if (mState != STATE_NORMAL) {
                    return;
                }
                //判斷是否最後一item個顯示出來
                LayoutManager layoutManager = getLayoutManager();

                //可見的item個數
                int visibleChildCount = layoutManager.getChildCount();
                if (visibleChildCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && !isLoadMore) {
                    View lastVisibleView = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
                    int lastVisiblePosition = recyclerView.getChildLayoutPosition(lastVisibleView);
                    if (lastVisiblePosition >= layoutManager.getItemCount() - 1) {
                        footerView.setVisibility(VISIBLE);
                        isLoadMore = true;
                        if (myRecyclerViewListener != null) {
                            myRecyclerViewListener.onLoadMore();
                        }
                    } else {
                        footerView.setVisibility(GONE);
                    }
                }

            }
        });

源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章