在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
。
开始左右滑动以后,再也不容许下拉。下拉以后,再也不容许左右滑动。