在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: ListView
, GridView
, ScrollView
, ViewPager
,該項目爲這些View都作了適配。然而因爲設計的缺陷,每種View都要作相應的適配, 缺少定製性和擴展性,如今這個項目再也不維護了。佈局
這個項目的流行,對Android UI的表現是有必定影響的,和iOS 上各類新穎美妙的樣式相比,Android 的樣式老是顯得較爲遜色。post
對於經常使用ListView
有一種包含下拉刷新和加載更多的實現。這個實現是給ListView
加了一個Header View
,同時集成了加載更多功能。動畫
且不說把這兩個功能合併在一塊兒在設計上的缺陷,用Header View來實現下拉刷新,在UI體驗上就是大打折扣的。
最爲樸素直觀的想法和認知,下拉刷新的樣式佈局應該是這樣的:
+--------------------+
| +--------------+ |
| | Header View | |
| +--------------+ |
| +--------------+ | <----- PtrFrameLayout
| | | |
| | | |
| | | |
| | Content View | |
| | | |
| | | |
| | | |
| | | |
| | | |
| +--------------+ |
+--------------------+
外部是一個框架(PtrFrameLayout
), 內含 Header View 和 Content View。須要支持通用佈局屬性 padding 和 margin。
Header View 是頭部,展現刷新相關動畫。
Content View 是內容,好比ListView
, ScrollView
等。
在xml文件中,能夠方便指定Header View 和 Content View,也能夠經過Java代碼指定。
這樣的佈局設計,加上合理的接口抽象,能夠帶來極大的便利:
乾淨,高效。繼承於ViewGroup。能夠包含任何的View作爲Content View。
極大的定製性和可擴展性,能夠定製任何你要的樣式。類庫中內置了一些流行的樣式,可是你能夠輕鬆定製符合本身APP的樣式。
極大的靈活性。開放出接口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。初始狀態。Header View 在 PtrFrameLayout
界面以外。
位置2。隨着下拉,Header View 和 Content View 慢慢往下移動,到達位置2。
在位置2,釋放,Content View 回到初始位置。不會觸發刷新操做。
位置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個方法:
onRefreshBegin
。多種模式刷新模式能夠選,多種UI樣式可選。無論萬千變化,在開始刷新時,都會調用這個方法進行數據刷新。checkCanDoRefresh
。開發者面對的業務各不同,衆口難調,封裝變化:開發者能夠經過此方法,肯定能夠進行下拉刷新的時機。好比列表數據爲空,好比列表數據過時,好比嵌套在ViewPager中的某個Fragment中的一個列表數據爲空。 PtrUIHandler
這個接口關注UI的變化,這個接口使得實現一個UI樣式很是簡單。
onUIReset
。當位置回到初始位置。onUIRefreshPrepare
。當位置離開初始位置。onUIRefreshBegin
。開始刷新動畫。 onUIRefreshComplete
。刷新動畫完成。刷新完成以後,開始迴歸初始位置。
若是刷新完成須要播放一個動畫,動畫完成以後,纔開始迴歸到初始位置。PtrFrameLayout.setRefreshCompleteHook
會有所幫助。
onUIPositionChange
。 位置發生變化時此方法通知UI更新。
得益於這個接口的抽象,我才能在段時間內完成類庫內置的幾種樣式。
PtrFrameLayout
接受一個PtrHandler
和多個PtrUIHandler
。在下拉刷新的過程當中,你能夠經過很是靈活的方式展示出各類效果。
列表沒法向上滑動,老是下拉。PtrHandler.checkCanDoRefresh
這個方法實現有問題。
ListView中的長按。按住ListView一項,下拉鬆開後,接觸的那項仍然處於被按下狀態。PtrFrameLayout.setInterceptEventWhileWorking
。
和ViewPager橫向移動的配合: PtrFrameLayout.disableWhenHorizontalMove
。
開始左右滑動以後,再也不容許下拉。下拉以後,再也不容許左右滑動。