我眼中的下拉刷新

背景

在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

    开始左右滑动以后,再也不容许下拉。下拉以后,再也不容许左右滑动。

相关文章
相关标签/搜索