之前在做项目的时候,项目里面的下拉刷新控件是一个直接继承 ListView 实现的 PullToRefreshListView。由于是直接集成 ListView 实现的,所以只能在使用 ListView 的场景下才能使用,而且不能很方便的实现多种类型的下拉刷新方式,所以我就写了一个下拉刷新的控件 ASwipeRefresh。这个控件主要实现了两件功能:

  1. 支持自定义下拉刷新的 Header 和 上拉加载更多的 Footer
  2. 支持自定义 Header 和 Footer 的滑出方式

这两个功能是我认为比较重要的功能,因为通过组合上面提到的 1 和 2 就可以实现现在的大多数下拉刷新方式。对于现在用的比较多的下拉刷新方式,可以将其分为 4 种类型。一个下拉刷新控件可以分为 Header,Content,和 Footer 三部分,而这四种不同类型的刷新方式之间的主要不同点就是手指拖动的对应的 Header,Content 或 Footer 是否会滑动和这三个 View 在层级(也就是 Z 轴)上的关系。

按常见程度排序它们分别是:

  1. Normal refresh,普通下拉刷新

    1. Content(即要滑动的正文视图)滑动的时候,Header 和 Footer 是同步滑动的(即滑动的距离相等)。
    2. Content 和 Header,Footer 三者在同一层上(即在 Z 轴上是相等的)
  2. Drawer refresh,抽屉式下拉刷新

    1. Content(即要滑动的正文视图)滑动的时候,Header 和 Footer 是不同步滑动的(即滑动的距离不相等),一般常见为 Header 或 Footer 不滑动。
    2. Content 是在 Header 和 Footer 的上一层视图(即 Content Z 轴上是最大的,Z 轴值大的会覆盖住 Z 轴值小的视图)
  3. Material refresh

    因为这种下拉刷新的方式我最先是在 Google 的 Lollipop 上看到的,所以就命名为Material refresh。这种刷新方式和上面的 Drawer refresh 很类似,区别就是 Header 或 Footer 的 Z 轴值比 Content 的大而且 Content 不会滑动。

    1. Content(即要滑动的正文视图)滑动的时候,Header 和 Footer 是不同步滑动的(即滑动的距离不相等),一般常见为 Content 不滑动
    2. Content 是在 Header 和 Footer 的上一层视图(即 Content Z 轴上是最小的,Z 轴值大的会覆盖住 Z 轴值小的视图)
  4. Side in Refresh

    这种下拉刷新和 Material refresh 类似,区别就是 Header 或 Footer 改成了由侧面滑入。

    1. Content(即要滑动的正文视图)滑动的时候,Header 和 Footer 是不同步滑动的(即滑动的距离不相等),一般常见为 Content 不滑动
    2. Content 是在 Header 和 Footer 的上一层视图(即 Content Z 轴上是最小的,Z 轴值大的会覆盖住 Z 轴值小的视图)

所以这样看来的话,一个可以适应以上所有下拉刷新场景的控件,要满足以下功能:

  • 可以自定义 Header,Footer,Content 之间的层级关系(也就是 Z 轴的值)。在 ASwipeRefresh 中对应的是 LayoutLayer
  • 可以自定义 Header,Footer 从哪个方向滑动出来,同时还要可以自定义 Header,Footer的初始位置。在 ASwipeRefresh 中对应的分别是LayoutDirection,OffsetOrientation
  • 可以自定义 Header,Footer,Content 是否可以滑动。在 ASwipeRefresh 中对应的是 parallaxFactorparallaxFactor 是用来设置滑动时候对应 View 的视差因数的,这里只要设置为 0,对应的 View 也就不会滑动了
  • 当然还要可以自定义 Header 和 Footer,这里主要是把当前 ASwipeRefresh 滑动的 percent 和 status 传递过去。

除此之外,我还把是否可以进行刷新通过回调 isReadyToRefreshisReadyToLoadMore 暴露出来,这样就可以自定义滑动到什么位置才进行刷新。不过现在这个控件还是有缺点的,自动刷新那里处理的不是很好,由于不能缺点一个 View 什么时候已经 ready,所以找不到合适的位置去自动调用下拉刷新,现在的作法通过延时来实现,但其实很不好,无法保证能自动下拉成功,所以要实现自动下拉刷新的话,建议在外部合适的位置手动调用 refreshStart