簡單點,基於Ultra-Pull-To-Refresh的下拉刷新上拉加載再封裝

開始前老是要有廢話的

很早以前,上拉加載下拉刷新這種交互方式一經推出,就火炸了。若是你在兩三年前就接觸過android開發,你必定據說過PullToRefreshListView這個開源框架,使用起來很簡單,首先感謝偉大的做者開源這麼優秀的做品,可是對於新手來說,這個框架有些過於龐大了,類和方法實在太多,定製功能太複雜,而且不得不說,再使用過程當中,這個框架的侷限性很大,說到底他只是個ListView,當你和其餘可滑動控件一塊兒套用時,就會出現各類的問題,而且這個框架的做者已經好幾年沒有維護過他了。
我在git上嘗試查找能替代他的傢伙,萬幸這個傢伙被我成功發現了。雖然他的歲數也已經很大了,但到如今我仍然在使用而且也一直在維護他的代碼。android-Ultra-Pull-To-Refresh,一樣感謝liaohuqiu貢獻這麼優秀的做品。我習慣叫這個框架爲Ptr,至於爲何到如今我還要推薦這個框架,請聽我慢慢道來:javascript

  • 首先他不是個ListView也不是個GridView,他是個ViewGroup,這意味着什麼?他意味着你的整個ViewGroup都是能夠下拉的,你的ViewGroup裏能夠裝任何內容,TextView,Button,ListView,RecycleView均可以,他不在侷限我只是個ListView,簡單說,下拉這個動做,不綁定任何控件,他是獨立的。這個優勢我給滿分。
  • 充分抽象解耦合,咱們能夠定義屬於咱們本身的刷新樣式,只要實現統一接口,定製你的樣式沒有那麼難,典型的面向對象思想。
  • 類相對較少。你說這也是個優勢?我說這是個大大的優點,類少代碼易讀,核心類集中,修改與定製會很方便。

那麼他沒有缺點嗎?固然有!java

  • 或許這也不算是個缺點,在做者公佈的開源項目中,並不支持上拉加載更多功能,至於爲何,做者在他的issues裏已經回覆過了,大概意思是:下拉刷新和加載更多,不是同一個層級的功能。加載更多不該該由 UltraPTR 去實現,而應該由 Content本身去實現。不要緊做者的另一個開源庫,有實現這一功能,在這篇文章中,咱們把他整合在一塊兒!
  • 做者源碼中的實現使用的是ListView,這裏咱們會換成RecycleView,畢竟要與時俱進嘛!
  • 一樣在滑動嵌套中,會有衝突的問題。不要緊,代碼中已經解決了一些。

看完這篇文章你會獲得什麼?

我不會對做者的源碼進行解析,畢竟網上的輪子已經不少了,重複造輪子是可恥的。也不會把修改源碼的過程進行講解,爲啥?由於很早以前就改好了,到如今已經忘了具體的過程。。。哈!Sorry,這篇文章我會盡力展現框架的結構和使用方式,幫助你更好理解Ptr的優點,思路是最重要的,代碼並不重要!android

開始幹活啦

gif中我展現了上拉加載,下拉刷新,標準,所有功能,空View五種狀況。這裏只是爲了展現,其實咱們在使用過程當中,基本不會用到各類狀況之間的互相切換。項目中目前只定義了兩種樣式的頭部,第一種就是相似gif中MaterialDesign的樣式,第二種就是傳統樣式相似PullToRefreshListView中的,這裏沒有展現。git

1、使用指南

1.佈局使用

<com.leinyo.superptrwithrv.widget.ptr.PullToRefreshView
        android:id="@+id/pull_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:ptr_refresh_mode="none"
        app:ptr_scrollable="true"
        />複製代碼

此處不廢話,講解屬性含義:github

<declare-styleable name="PullToRefreshView">
        <attr name="ptr_refresh_mode" format="enum">
            <enum name="none" value="0"/>
            <enum name="pull_from_start" value="1"/>
            <enum name="pull_from_end" value="2"/>
            <enum name="both" value="3"/>
        </attr>
        <attr name="ptr_check_login" format="boolean"/>
        <attr name="ptr_header_mode" format="enum">
            <enum name="material" value="0"/>
            <enum name="normal" value="1"/>
        </attr>
        <attr name="ptr_padding_left" format="integer"/>
        <attr name="ptr_padding_right" format="integer"/>
        <attr name="ptr_scrollable" format="boolean"/>
    </declare-styleable>複製代碼
  • ptr_refresh_mode 刷新方式
    none 標準的RecycleView
    pull_from_start 支持下拉刷新
    pull_from_end 支持上拉加載
    both 上拉加載下拉刷新
  • ptr_check_login 是否檢查登陸狀態 只有登陸(這部分代碼本身實現)纔會觸發下拉刷新功能
  • ptr_header_mode 下拉刷新頭樣式
    material gif中演示的樣式
    normal 標準模式
  • ptr_padding_left ptr_padding_right 設置RecycleView的padding
  • ptr_scrollable 是否顯示滾動條

2.刷新動做對應回調

下拉刷新對應回調接口:設計模式

public interface OnPullRefreshListener {
        void onPullRefresh();
    }複製代碼

onPullRefresh()方法會在刷新動畫達到臨界值之後回調。
取消下拉動畫方法:架構

public void onPullRefreshComplete() {
        mPtrFrameLayout.refreshComplete();
    }複製代碼

上拉加載對應回調接口:app

public interface OnLoadMoreListener {
        void onLoadMoreRefresh();
    }複製代碼

取消上拉動畫方法:框架

public void onLoadMoreComplete(boolean hasMore) {
        mLoadMoreRecyclerViewContainer.loadMoreFinish(hasMore);
    }複製代碼

boolean值 表明是否還能夠繼續上拉 false 不會再回調onLoadMoreRefresh()!!!佈局

both對應回調接口:

public interface OnRefreshListener {
        void onPullRefresh();

        void onLoadMoreRefresh();
    }複製代碼

取消所有動畫方法:

public void onLoadComplete(boolean hasMore) {
        mLoadMoreRecyclerViewContainer.loadMoreFinish(hasMore);
        if (mCurrentRefreshMode == REFRESH_FROM_START) {
            if (isRefreshing()) {
                mPtrFrameLayout.refreshComplete();
            }
        }
    }複製代碼

3.添加EmptyView

若是咱們的返回的數據是空的,須要顯示一個空頁面。咱們不須要控制兩個View的show與gone,ListView能作的咱們一樣能到。
mPullView.addEmptyView(mEmpty);
注意:addEmptyView方法不能在顯示以前設置,不然會先顯示空View。爲啥,後面再說。

4.添加頭HeadView

一樣ListView能作的咱們也能夠。
mPullView.addHeaderView(mHeadView);

2、架構層次

這一部分必定是要上圖的,說多少都是無心的。

這部分你最好對Ptr有了解

  • PtrFrameLayout
    最外層ViewGroup,只負責下拉刷新,首先接觸到觸摸事件,符合下拉邏輯,則顯示頭佈局,不然向下分發事件
  • LoadMoreRecyclerViewContainer
    第二層ViewGroup,只負責上拉加載,監聽上拉事件,符合邏輯,通知RecyclerView繪製Foot佈局。
  • RecyclerView
    最後一層,我只負責顯示佈局,包括Head,EmptyView,Foot和正常的數據佈局。

怎麼樣?一張圖是否是已經足夠清楚了?看到如今你是否已經感嘆做者的設計能力了?再看看上面說到做者在issues裏已經回覆的:下拉刷新和加載更多,不是同一個層級的功能。加載更多不該該由 UltraPTR 去實現,而應該由 Content本身去實現。
每一層級,只對應本身的業務邏輯,並不關心其餘人在幹什麼,這就叫單一職責,解耦合。

原本以爲這裏應該是講得最多的,可是寫到這裏,發現實在沒啥可說的了,若是你真的看過Ptr的源碼,相信到這裏你也已經沒有疑惑了,並且有種神清氣爽的感受,其實就是這麼簡單,代碼設計很重要,這也是爲啥有的代碼讓人看得很爽,有的代碼讓人看了想吐

3、顯示佈局

咱們上面說過真正負責顯示佈局的是RecyclerView了,怎麼顯示?固然是交給Adapter就能夠了,多佈局顯示使用RecyclerView.Adapter的getItemViewType(int position)。須要記住的是:
咱們抽出基類BaseRefreshAdapter,裏面對多佈局顯示有一些基礎的操做,須要子類繼承咱們抽出基類BaseRefreshAdapter,並實現onCreateHolder(ViewGroup parent, int viewType)(他等同於onCreateViewHolder(ViewGroup parent, int viewType))和onBindHolder(VH holder, int position)(等同於onBindViewHolder(RecyclerView.ViewHolder holder, int position)),在子類中你只須要關心你本身的Item佈局就能夠了,其餘交給BaseRefreshAdapter中的邏輯就能夠了。
這裏面有泛型定義,看看源碼很好理解。
這就不難理解爲啥上面說過addEmptyView方法不能在顯示佈局以前設置了,由於若是你初始化就執行addEmptyView方法,那麼當adapter初始化時,就會執行onCreateViewHolder()一系列方法,這樣立刻就會顯示EmptyView了,等你的真正須要的數據返回纔會刷新數據佈局。

最後也仍是要有些廢話的

本人並未側重講解怎麼封裝,怎麼修改源碼適配RecyclerView,也並未拆源碼講解Ptr原理,也但願你們能理解,時間有點緊,並且輪子不少,不想再造了。
我但願若是你看到最後,這篇文章會對你有所幫助,學習人家的設計模式的同時,你也獲得了一個支持多種功能,基於RecyclerView的再封裝Ptr框架(感受好繞口啊),至於爲什麼叫SuperPtr,純碎是爲了好玩。。。
若是您在使用過程當中,有疑問和改進意見歡迎聯繫我。
SupterPtr 對您的支持我深表感謝,喜歡請您star哦~~

相關文章
相關標籤/搜索