我眼中的下拉刷新

背景

在APP交互中,下拉刷新是很是常見的一種交互方式。在使用APP的時候,這也成爲了一種潛意識的操做了。html

下拉刷新最先在iOS中出現,iOS的視圖渲染機制完成這種效果是很是簡單的。android

但Android的視圖呈現形式,實現這一效果就須要稍微麻煩一些了。git

兩三年以前,Android 的類庫打包,對自定義組件的支持是很弱的。res-auto 這樣的xml佈局屬性命名空間是在SDK Tool Version 17 中才支持的,那是2012年3月的事情。github

對於有資源的類庫,早先的打包形式是apklib格式。框架

apklib 格式的類庫使用 maven-android-plugin 生成。使用apklib格式的類庫,須要藉助maven。因此對於使用eclipse的開發者來講,這又增長了一道門檻。這使得apklib一直沒有流行起來。eclipse

aar格式是在 Google I/O 2013 才提出的。maven-android-plugin 3.7 版本開始支持aar格式。Android Studio 1.0的發佈和流行,標誌着apklib格式將要退出歷史舞臺。maven

在帶資源的的類庫不能被很好支持的時代,自定義組件的封裝和實現也受到了極大的限制。Android-PullToRefresh 的實現即是這一限制的一種體現。爲了支持經常使用的幾種View: ListViewGridViewScrollViewViewPager,該項目爲這些View都作了適配。然而因爲設計的缺陷,每種View都要作相應的適配, 缺少定製性和擴展性,如今這個項目再也不維護了。佈局

這個項目的流行,對Android UI的表現是有必定影響的,和iOS 上各類新穎美妙的樣式相比,Android 的樣式老是顯得較爲遜色。post

對於經常使用ListView 有一種包含下拉刷新和加載更多的實現。這個實現是給ListView加了一個Header View,同時集成了加載更多功能。動畫

且不說把這兩個功能合併在一塊兒在設計上的缺陷,用Header View來實現下拉刷新,在UI體驗上就是大打折扣的。

迴歸簡單

最爲樸素直觀的想法和認知,下拉刷新的樣式佈局應該是這樣的:

+--------------------+
               |  +--------------+  |
               |  | Header View  |  |
               |  +--------------+  |
               |  +--------------+  |  <----- PtrFrameLayout
               |  |              |  |
               |  |              |  |
               |  |              |  |
               |  | Content View |  |
               |  |              |  |
               |  |              |  |
               |  |              |  |
               |  |              |  |
               |  |              |  |
               |  +--------------+  |
               +--------------------+
  1. 外部是一個框架(PtrFrameLayout), 內含 Header View 和 Content View。須要支持通用佈局屬性 padding 和 margin。

  2. Header View 是頭部,展現刷新相關動畫。

  3. Content View 是內容,好比ListViewScrollView等。

  4. 在xml文件中,能夠方便指定Header View 和 Content View,也能夠經過Java代碼指定。

這樣的佈局設計,加上合理的接口抽象,能夠帶來極大的便利:

  1. 乾淨,高效。繼承於ViewGroup。能夠包含任何的View作爲Content View。

  2. 極大的定製性和可擴展性,能夠定製任何你要的樣式。類庫中內置了一些流行的樣式,可是你能夠輕鬆定製符合本身APP的樣式。

  3. 極大的靈活性。開放出接口checkCanDoRefresh(),檢測什麼時候能夠進行下拉刷新。即便再複雜的佈局嵌套,也能應付自如。

2013年, 初接觸Android的時候,便完成了這樣的設計的雛形。這個設計在Cube-SDK靜靜地躺着,直到support-v4-r21發佈,我看到了SwipeRefreshLayout

因而我從Cube-SDK 中把下拉刷新獨立出來。這就有了android-Ultra-Pull-to-Refresh

交互

Header View 和 Content View 的位置關係以下圖:

+--------------+                                                刷新距離
       | Header View  |                                    +-------------------
       +--------------+               +--------------+     |             
    +--------------------+         +--| Header View  |--+  |     +--------------------+     
    |  +--------------+  |         |  +--------------+  |  |     |                    | 
    |  |              |  |         |  +--------------+  |  v     |  +--------------+  |  
  --|--|--------------|--|---   ---|--|--------------|--|---  ---|--| Header View  |--|--
    |  |              |  |         |  |              |  |        |  +--------------+  |    
    |  | Content View |  |         |  |              |  |        |  +--------------+  |    
    |  |              |  |         |  | Content View |  |        |  |              |  |    
    |  |              |  |         |  |              |  |        |  |              |  |    
    |  |              |  |         |  |              |  |        |  |              |  |    
    |  |              |  |         |  |              |  |        |  | Content View |  |    
    |  |              |  |         |  |              |  |        |  |              |  |    
    |  |              |  |         |  |              |  |        |  |              |  |    
    |  +--------------+  |         |  |              |  |        |  |              |  |    
    +--------------------+         +--|--------------|--+        +--|--------------|--+    
                                      +--------------+              |              |       
                                                                    |              |       
                                                                    +--------------+       

             位置1                         位置2                         位置3
            初始狀態                    未達刷新距離                  達到刷新距離

位置關係

手指觸摸屏幕往下拉動,Header View 和 Content View 也往下移動。

在往下移動的過程當中,Header View 和 Content View 的位置關係如上圖所示:

  1. 位置1。初始狀態。Header View 在 PtrFrameLayout 界面以外。

  2. 位置2。隨着下拉,Header View 和 Content View 慢慢往下移動,到達位置2。

    在位置2,釋放,Content View 回到初始位置。不會觸發刷新操做。

  3. 位置3。繼續下拉,Content View 頭部越過刷新線, 從位置2到到達位置3。在位置3,釋放將會觸發刷新。

距離的定義

  • 下拉距離(Offset)

    在往下移動的過程當中,Content View 上邊界距離 PtrFrameLayout 上邊界的距離,咱們稱爲下拉距離。這是一個大於等於0的值。

  • 刷新距離(Offset to Refresh)

    下拉距離達到必定距離以後,釋放, 將會觸發刷新。這個距離咱們稱爲刷新距離

  • 刷新時保持頭部的距離(Offset to Keep Header While Loading)

    在刷新時,會顯示Header View,Header View上顯示loading 動畫,提示用戶用戶正在加載數據。

    這個時候的下拉距離,咱們稱爲刷新時保持頭部的距離,這個距離通常是頭部的高度,可是隻要你願意,你能夠自定義。

下拉刷新和釋放刷新

  • 下拉刷新(PullToRefresh)

    若是設置爲下拉刷新,從位置2到位置3的過程,一旦達到刷新距離,即開始刷新操做。

  • 釋放刷新(ReleaseToRefresh)

    若是設置爲釋放刷新,從位置2到位置3的過程,不觸發刷新操做,釋放觸發刷新操做。

    若是從位置3往上推,回到位置2,釋放,將不會觸發刷新操做。

刷新時保持頭部 (KeepHeaderWhileRefresh)

  • 若是不設置刷新時保持頭部。無論下拉刷新仍是釋放刷新,釋放以後,迴歸到初始位置。

  • 若是設置刷新時保持頭部,若是釋放時下拉距離大於刷新時保持頭部的距離,會滑動到刷新時保持頭部的距離,並保持位置不動。

    刷新完成以後,迴歸到頭部位置。在刷新數據過程當中:

    • 若是下拉使得距離超過刷新時保持頭部的距離, 釋放後,會繼續迴歸到刷新時保持頭部的距離;刷新完成後,迴歸初始位置。
    • 若是上推使得距離小於刷新時保持頭部的距離, 釋放後,位置不動。刷新完成以後,迴歸初始位置。

自動刷新(AutoRefresh)

用戶手動下拉,能夠實現數據更新。同時,也容許程序調用,展現刷新UI,實現自動刷新。

自動刷新開始,Header View 和 Content View 自動下滑,達到刷新時保持頭部的距離以後,停留。

後續行爲,同刷新時保存頭部行爲一致。

自動刷新有兩種模式:

  • 馬上自動刷新

    從離開初始位置開始,即開始進入刷新狀態

  • 不馬上自動刷新

    刷新時保持頭部的距離以後,纔開始進入刷新狀態。

接口和實現

  • PtrHandler

    這個是開發者最常接觸的接口,這個接口關注業務的變化。其包含2個方法:

    1. onRefreshBegin。多種模式刷新模式能夠選,多種UI樣式可選。無論萬千變化,在開始刷新時,都會調用這個方法進行數據刷新。
    2. checkCanDoRefresh。開發者面對的業務各不同,衆口難調,封裝變化:開發者能夠經過此方法,肯定能夠進行下拉刷新的時機。好比列表數據爲空,好比列表數據過時,好比嵌套在ViewPager中的某個Fragment中的一個列表數據爲空。
  • PtrUIHandler這個接口關注UI的變化,這個接口使得實現一個UI樣式很是簡單。

    1. onUIReset。當位置回到初始位置。
    2. onUIRefreshPrepare。當位置離開初始位置。
    3. onUIRefreshBegin。開始刷新動畫。
    4. onUIRefreshComplete。刷新動畫完成。刷新完成以後,開始迴歸初始位置。

      若是刷新完成須要播放一個動畫,動畫完成以後,纔開始迴歸到初始位置。PtrFrameLayout.setRefreshCompleteHook 會有所幫助。

    5. onUIPositionChange。 位置發生變化時此方法通知UI更新。

      得益於這個接口的抽象,我才能在段時間內完成類庫內置的幾種樣式。

    PtrFrameLayout 接受一個PtrHandler 和多個PtrUIHandler。在下拉刷新的過程當中,你能夠經過很是靈活的方式展示出各類效果。

一些疑難問題

  1. 列表沒法向上滑動,老是下拉。PtrHandler.checkCanDoRefresh 這個方法實現有問題。

  2. ListView中的長按。按住ListView一項,下拉鬆開後,接觸的那項仍然處於被按下狀態。PtrFrameLayout.setInterceptEventWhileWorking

  3. 和ViewPager橫向移動的配合: PtrFrameLayout.disableWhenHorizontalMove

    開始左右滑動以後,再也不容許下拉。下拉以後,再也不容許左右滑動。

相關文章
相關標籤/搜索