1. 程式人生 > >輕鬆實現RecycleView的下拉重新整理、載入更多

輕鬆實現RecycleView的下拉重新整理、載入更多

PullRefresh.gif

那如同這個題目,這裡面涉及的東西其實還是比較多的,RecycleView SwipeRefreshLayout,下拉重新整理(這個就是SwipeRefreshLayout的),載入更多。

SwipeRefreshLayout

這個是Google自己封裝的一個下拉重新整理的控制元件,裡面使用了5.0開始的巢狀滑動機制,有興趣的朋友可以去看看原始碼!使用起來其實就涉及到以下方法:

setOnRefreshListener() 下拉重新整理的相關回調。

setRefresh() 通知是否開始重新整理或者重新整理完成。(坑1)

setColorSchemeColors()

loading的時候progressbar的顏色,支援多個。

SwipeRefreshLayout的坑

進入頁面呼叫setRefresh(true),更不不顯示重新整理的小圓圈?!
簡單的說,這個就是在oncreate()方法執行的時候,view還沒有繪製出來,這個時候你設定重新整理不重新整理其實都一樣的,解決方法,post一下!

mRecyclerView.post(new Runnable() {
        @Override
        public void run() {
            mRefreshLayout.setRefreshing(refresh);
        }
    });

RecycleView

RecycleView其實出現都有一定的年頭了,前幾天公司來面試的居然說他還沒有用過。。這個也是醉醉的!

RecycleViewListView的強力升級!加入了holder便於管理和複用相同的型別。

就我目前掌握的情況,RecycleView對於ListView有了以下的不同:

1、加入了LayoutManager用用管理各種型別的佈局,而且通過不同的佈局可以實現橫向、豎向、瀑布式的等各種複雜的佈局。

2、加入Holder來管理相關佈局和複用,對於每一種Type的View你都要建立一個對應的Holder來管理它!

3、取消了header和bottom佈局。

4、沒有現成的itemClick回撥。

5、引入了豐富的動畫效果。(坑4)

6、添加了豐富的資料重新整理的方法,可以區域性重新整理了!(坑3)

7、可自定義相關分割線。

8、支援swipe刪除和drag排序。(ItemTouchHelper 幫助類)

9、預設是不顯示scrollBar的(坑2)

10、可以設定不同型別holder佔據不同的空間(ItemColumnSpan)

上面這些不是所有的都講,其實本文主要涉及的就是相關adapter,裡面對應不同的holder,及相關的封裝。然後說說踩了哪些坑。

基本思路

  • 1、明確什麼時候開始載入更多?

下拉重新整理就呼叫SwipeRefreshLayout相關就好了,那麼載入更多呢?這個就要自己去寫相關的佈局了。然後第一個問題,什麼時候載入更多??因為RecycleView有各種佈局,所以判斷最後一個也是要區分不同的adapter的!

  • 2、載入更多有多少種情況?
    大致有三種,正在載入更多;載入更多錯誤;沒有更多資料了;

具體實現

1、監聽滑動,滿足條件開始載入更多。

        @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (null != scrollListener) {
            scrollListener.onScrolled(SwipeRefreshRecycleView.this, dx, dy);
        }
        if (null == manager) {
            throw new RuntimeException("you should call setLayoutManager() first!!");
        }
        if (null == adapter) {
            throw new RuntimeException("you should call setAdapter() first!!");
        }
        if (manager instanceof LinearLayoutManager) {
            int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) manager).findLastCompletelyVisibleItemPosition();

            if (adapter.getItemCount() > 1 && lastCompletelyVisibleItemPosition >= adapter.getItemCount() - 1 && adapter.isHasMore()) {
                adapter.isLoadingMore();
                if (null != listener) {
                    listener.onLoadMore();
                }
            }
            int position = ((LinearLayoutManager) manager).findFirstVisibleItemPosition();
            if (lastTitlePos == position) {
                return;
            }
            lastTitlePos = position;
        }
        if (manager instanceof StaggeredGridLayoutManager) {
            int[] itemPositions = new int[2];
            ((StaggeredGridLayoutManager) manager).findLastVisibleItemPositions(itemPositions);

            int lastVisibleItemPosition = (itemPositions[1] != 0) ? ++itemPositions[1] : ++itemPositions[0];

            if (lastVisibleItemPosition >= adapter.getItemCount()  && adapter.isHasMore()) {
                adapter.isLoadingMore();
                if (null != listener) {
                    listener.onLoadMore();
                }
            }

        }

    }

2、定義自己的載入更多的ViewHolder。

3.定義相關的方法實時更新ViewHolder的三種狀態。

public class NewBottomViewHolder extends RecyclerView.ViewHolder{
    @Bind(R.id.footer_container)
    public LinearLayout contaier;

    @Bind(R.id.progressbar)
    ProgressBar pb;
    @Bind(R.id.content)
    TextView content;
    @Nullable
    private final SwipeRefreshRecycleView.OnRefreshLoadMoreListener mListener;

    public NewBottomViewHolder(View itemView, SwipeRefreshRecycleView.OnRefreshLoadMoreListener listener) {

        super(itemView);
        ButterKnife.bind(this,itemView);
        mListener = listener;
    }

    public void bindDateView(int state) {
        switch (state) {
            case AdapterLoader.STATE_LASTED:
                contaier.setVisibility(View.VISIBLE);
                contaier.setOnClickListener(null);
                pb.setVisibility(View.GONE);
                content.setText("---  沒有更多了  ---");
                break;
            case AdapterLoader.STATE_LOADING:
                contaier.setVisibility(View.VISIBLE);
                content.setText("載入更多!!");
                contaier.setOnClickListener(null);
                pb.setVisibility(View.VISIBLE);
                break;
            case AdapterLoader.STATE_ERROR:
                contaier.setVisibility(View.VISIBLE);
                pb.setVisibility(View.GONE);
                content.setText("--- 載入更多失敗點選重試 ---");
                contaier.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (mListener != null) {
                            mListener.onLoadMore();
                        }
                        content.setText("載入更多!!");
                        pb.setVisibility(View.VISIBLE);
                    }
                });
                break;
        }
    }

}

4.定義相關擴充套件方法便於使用者自己定義底部佈局及相關狀態處理。

這裡就必須詳細講講Adapter裡面的相關方法了!

getItemCount(),在RecycleView知道它一共有多少數量的Item需要展示,返回0之後不會執行剩餘的方法!

onCreateViewHolder(ViewGroup parent, int viewType),某種Type的Holder第一次建立的時候會呼叫該方法,當然沒有複用的時候也會去建立,一旦複用了,改方法不會再執行了!

onBindViewHolder(RecyclerView.ViewHolder holder, int position),每一次更新對應itemView的時候都會呼叫該方法,所以在該方法中要實時的重新整理資料!(因為存在複用,所以重新整理的時候一定要徹底!!!

以上三個方法是必須實現的,因為在父類adapter裡是抽象滴!

還有一個方法也比較重要:

getItemViewType(int position),這個方法是返回對應pos的型別的,如果你只有一個型別,不需要重寫該方法,預設返回的是0。

是不是這麼說起來比ListView還要爽一點兒?不用去判斷什麼convertView==null!

@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case TYPE_BOTTOM:
            if (loadMore != null) {
                RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);
                if (holder == null) {
                    throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");
                }
                return holder;
            } else {
                return new BottomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_footer, parent, false));
            }
        default:
            return onViewHolderCreate(parent, viewType);
    }

}

@Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (getItemViewType(position) == TYPE_BOTTOM) {
        loadState = loadState == STATE_ERROR ? STATE_ERROR : isHasMore() ? STATE_LOADING : STATE_LASTED;
        if (loadMore != null) {
            try {
                onBottomViewHolderBind(holder, loadState);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                ((BottomViewHolder) holder).bindDateView(loadState);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    } else {
        onViewHolderBind(holder, position);
    }
}

這裡在RefreshRecycleAdapter<T>中實現了剛剛說的三個方法,並且相關的已經加final修飾了!所以之後你只需要實現如下方法來完成你自己的item填充就好了:

   RecyclerView.ViewHolder onViewHolderCreate(ViewGroup parent, int viewType);

void onViewHolderBind(RecyclerView.ViewHolder holder, int position);

對於載入更多的幾種狀態的更改,提供如下的相關方法!

boolean isHasMore();

void isLoadingMore();

void loadMoreError();

對於建立自己制定的載入更多的佈局,提供如下方法擴充套件!

void setLoadMoreView(View view);

RecyclerView.ViewHolder onBottomViewHolderCreate(View loadMore);

void onBottomViewHolderBind(RecyclerView.ViewHolder holder, int loadState);

還沒有說的那就是資料來源相關的方法。提供了set和append兩種方式!

void setList(List<T> data);

void appendList(List<T> data);

@Override
public final void appendList(List<T> data) {
    int positionStart = list.size();
    list.addAll(data);
    int itemCount = list.size() - positionStart;

    if (positionStart == 0) {
        notifyDataSetChanged();
    } else {
        notifyItemRangeInserted(positionStart + 1, itemCount);
    }
}

還是那話,這些方法都是RefreshRecycleAdapter<T>裡面寫好的,我們寫自己的adapter時更本不用去care!只需要去呼叫setList()或者appendList()就好了!!

說到這裡不得不提提RecycleView重新整理資料的相關方法和坑!

notifyDataSetChanged()的基礎上, RecycleView增加了一系列的方法用於增刪改。。

notifyItemInserted();
notifyItemRangeInserted();

notifyItemChanged();
notifyItemRangeChanged();

notifyItemRemoved();
notifyItemRangeRemoved();

在使用的過程中,發現呼叫notifyItemChanged()之後不會去執行onBindViewHolder(),(坑3 坑4)這個就導致重新整理沒有觸發了!最後搜到的結果是因為mRecyclerView.setItemAnimator(new DefaultItemAnimator())引起的,解決方案是複寫相關方法

@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {
    return true;
}

小結

首先通過addList()或者appendList()的方法設定相關資料來源。

滑動過程中需要載入更多時回撥相關方法,並在adapter中通知相關狀態重新整理。

 adapter.isLoadingMore();
 if (null != listener) {
    listener.onLoadMore();
 }

載入錯誤的時候呼叫相關的方法通知狀態改變。

adapter.loadMoreError();

建立BottomHolder的時候判斷有沒有設定自定義的view,如果有,那麼就去走子類的onBottomViewHolderCreate()方法建立自定義的Bottomholder,然後實時更新相關資料!

    @Override
public final void setLoadMoreView(@NonNull View view) {
    loadMore = view;
}

if (loadMore != null) {
    RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);
    if (holder == null) {
        throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");
    }
    return holder;
        } 

最後在onBottomViewHolderBind(RecyclerView.ViewHolder holder, int state)的方法中執行bindDateView(state)實時重新整理相關的狀態。

public void bindDateView(int state) {
    switch (state) {
        case AdapterLoader.STATE_LASTED:
            contaier.setVisibility(View.VISIBLE);
            contaier.setOnClickListener(null);
            pb.setVisibility(View.GONE);
            content.setText("---  沒有更多了  ---");
            break;
        case AdapterLoader.STATE_LOADING:
            contaier.setVisibility(View.VISIBLE);
            content.setText("載入更多!!");
            contaier.setOnClickListener(null);
            pb.setVisibility(View.VISIBLE);
            break;
        case AdapterLoader.STATE_ERROR:
            contaier.setVisibility(View.VISIBLE);
            pb.setVisibility(View.GONE);
            content.setText("--- 載入更多失敗點選重試 ---");
            contaier.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mListener != null) {
                        mListener.onLoadMore();
                    }
                    content.setText("載入更多!!");
                    pb.setVisibility(View.VISIBLE);
                }
            });
            break;
    }
}

PS 最後還有預設是不顯示scrollBar的問題,這個問題,似乎必須在xml裡面配置,不能程式碼直接new RecycleView。然後可以使用相關程式碼控制ScrollBar是否顯示!

mRecyclerView.setVerticalScrollBarEnabled(true)

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycle_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

另外對於特性8、10這裡就不詳細介紹了,滑動刪除和拖拽排序在TouchHelperCallback中有相關支援!方法如下:

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    if (callBack != null) {
        callBack.onItemMove(viewHolder.getAdapterPosition(),
                target.getAdapterPosition());
    }
    return true;
}

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    if (callBack != null) {
        callBack.onItemDismiss(viewHolder.getAdapterPosition());
    }
}

gradle快速整合

  compile 'com.lovejjfg.powerrecycle:powerrecycle:1.0.0'

相關下載

—- Edit By Joe —-

相關推薦

輕鬆實現RecycleView重新整理載入

那如同這個題目,這裡面涉及的東西其實還是比較多的,RecycleView SwipeRefreshLayout,下拉重新整理(這個就是SwipeRefreshLayout的),載入更多。 SwipeRefreshLayout 這個是Google自己封

自己封裝的工具類,使用原生SwipeRefreshLayout+RecycleView實現重新整理載入

實現SwipeRefreshLayout+RecycleView實現重新整理 在你的xml檔案裡寫上如下程式碼: <android.support.v4.widget.SwipeRefreshLayout

第一次嘗試用BaseRecyclerViewAdapterHelper實現重新整理載入新增頭佈局尾佈局等等功能

BaseRecyclerViewAdapterHelper 簡介: 減少重複 Adapter 程式碼 新增 Item 的點選事件,長按事件以及子控制元件的點選事件 新增頭部、尾部,下拉重新整理、上拉載入(上拉載入的5種載入更多動畫任你選擇,後期會新增更多的載入動畫)、沒有更多

高效能的給RecyclerView新增重新整理載入動畫,基於ItemDecoration(一)

專案已經上傳Github,點選這裡檢視,裡面有整個專案的效果。 先說說我為什麼要做這個。github上有一個比較火的開源專案,PullToRefreshView,估計不少人都看過,這個專案實現的動畫很漂亮,但是它有一個我無法忍受的缺點,就是當列表可以向下滾動的

retrofit+rxjava+recyclerview+重新整理+自動載入

安卓開發過程中,網路請求與下拉重新整理分頁列表的控制元件幾乎可以說是必不可少的,但是每次開發一款產品都要重新開發,肯定是不可取的,那麼最好是可以自己整理一個開發框架,那麼以後開發,直接引入專案即可 網路框架的封裝,從httpclient,到xutils,再到volley

微信小程式實戰篇-重新整理載入

下拉重新整理 實現下拉重新整理目前能想到的有兩種方式 呼叫系統的API,系統有提供下拉重新整理的API介面   下拉重新整理API.png 監聽scroll-view,自定義下拉重新整理,還記得scroll-view裡面有一個bindscrollto

微信小程式重新整理載入

呼叫系統的API,系統有提供下拉重新整理的API介面 *.js裡面://獲取應用例項 const app = getApp() var page = 1; var isfinish = false; //載入完畢 function loadmore(that) { if

Android各種View的重新整理載入

1.基本介紹 2.效果圖 3.使用示例程式碼 佈局檔案 <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schem

android 重新整理+底部載入 QJPageReloadView使用

前:本文為QiaoJim原創,轉載請附原文連結,謝謝合作! ----------------------------------------------------------------------------------------------- 本篇主要簡單介紹QJ

Android studio 重新整理載入使用LoadingViewFinal

Android studio已經普遍了,開發過程中會使用到下拉重新整理和載入更多,類似PULL-to-Refresh,xrecyclerview,SwipeRefreshLayoutd 等等。 現在

PullRefresh(自定義控制元件,ListView,重新整理載入)

PullRefresh 效果展示 Usage 一.layout <!--直接在佈局中申明控制元件--> <RelativeLayout xmlns:andr

PullToRefreshView重新整理上來載入,支援任何子view!

最新自己寫了一個PullToRefreshView,這是一個自定義view,繼承於LinearLayout,子控制元件可以是任意控制元件!先上一張福利圖: 由於PullToRefreshView繼承於LinearLayout,它有著ViewGroup的特性,子控制元件可以

recyclerview重新整理載入

SwipeRefreshLayout是google推薦的支援下拉重新整理的佈局控制元件,但是隻支援下拉重新整理不能上劃載入更多。我觀察知乎的重新整理和載入更多操作,發現他的載入更多是不需要上拉手勢的,當recyclerview滑到最後一個item時自動載入更多資

詳解自主實現RecyclerView重新整理載入HeaderFooter以及swiperefreshlayout的部分講解

  年前年後的那段時間比較忙,忙的來忘了寫部落格。最近空閒了,有時間了,來看部落格發現有兩個來月沒有發文章了,對自己的沒有堅持先來幾個,部落格還是要寫的,以後會持續更新。   廢話少說,網上關於列表控

XRecyclerView實現RecyclerView重新整理上來載入 自己做了部分修改,使程式碼簡潔易用

首先感謝 開源作者 /** * Created by jianghejie on 15/11/26. */ 的無私奉獻  我是在他的基礎上做的修改 以前一直是用的XListView   可是 測試發現有一個奇奇怪怪的BUG,我們還無法解決。最嚴重的是 XListV

支付寶賬單分組重新整理載入效果實現

          專案中賬單需要做二期優化,支付軟體嘛當然向支付寶看齊了。。分析了下支付的實現,github了一圈最後回到程式碼中,merge了兩個哥們的開源專案【開源就是好】。。 我主要分析一下整個merge的程式碼:    1:定義我們下拉重新整理和上拉載入的類【兩個

react-native FlatList 上重新整理 下載載入

import React,{Component} from 'react'; import {View,Text, Image, FlatList,RefreshControl} from 'react-native'; export default class HomeScreen extends Comp

[log] vue使用Mint元件實現重新整理載入

https://mint-ui.github.io/docs/#/zh-cn2/loadmore 使用的vue <mt-loadmore :top-method="loadTop" :bottom

better-scroll實現重新整理載入(巨簡單...)

1、廢話少說,先看看移動端的列表頁面 (1)正常瀏覽 (2)下拉重新整理中 (3)下拉重新整理結束 (4) 上拉載入 (5)上拉 載入中 3、網上也有一些基本使用教程,這裡就不一一累贅,下

詳解RecyclerView+BGARefreshLayout實現自定義重新整理載入和側滑刪除效果

前言 還有2個月就過年了,對於我們這樣在外漂泊的異鄉人來說,一家人團聚在一起,吃一頓團圓飯,那是再幸福不過的事了。我們之所以遠離家鄉來到異鄉就像異鄉人這首歌寫的一樣,只為一扇窗! 正文 上篇文章給大家講解了一下關於RecyclerView的使用,今天給