在SwipeRefreshLayout中加入多个子View

SwipeRefreshLayout是由Android官方提供的下拉刷新Widget。最低在v4中可用。

最近使用了一下,发现虽然是官方出品,但也还是不够理想。

原先尝试使用了Android L中提供的新支持库RecyclerView,两者之间的兼容性就不够好。

后来因为需要实现滑动到底部自动加载更多数据的功能,把RecyclerView换回了ListView。在使用FloatingActionButton这个库时,又发现了SwipeRefreshLayout不够方便。

根据FloatingActionButton的说明,需要将FloatingActionButton与ListView之类的放在同一ViewGroup下。

但是SwipeRefreshLayout只能有一个子视图,不然就会丢异常。

于是自然就会在SwipeRefreshLayout下加一个其他Layout来解决这个问题。

这样一来,异常的问题就解决了。但是运行后发现,ListView只能上滑,而不能下拉。一旦下拉,就会触发SwipeRefreshLayout的下拉刷新。

发现这个问题后,我知道是在事件派发上出了问题。下拉的事件在通常情况下应该由ListView来进行处理;当ListView位于顶部时,由SwipeRefreshLayout来进行处理。而现在的情况是全都由SwipeRefreshLayout来处理的。

既然知道问题在哪里,我就去查阅关于事件派发的资料。但是看完了之后也没有想到比较可行的解决方案。

接下来我去查看了一下SwipeRefreshLayout的源码,结果不怎么麻烦的就解决了。在这里我记录一下解决的思路和方法。

根据事件派发的知识,在SwipeRefreshLayout中相关的方法 onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent ev)。

查看它们的代码,发现在onIntercreptTouchEvent中有这么一段:

显然,canChildSrollUp跟我的问题有密切关联。再追看canChildSrollUp:

注释中也说了,如果子视图是自定义的,那么重写这个方法即可。mTarget就是SwipeRefreshLayout中默认的唯一的子视图。现在根据我的要求,继承SwipeRefreshLayout后,将这个方法改为如下:

 其中mScrollableChild是由自己定义的,可以滚动的View。

现在,下拉滚动的问题已经解决了。不过如果指定这个mScrollableChild还需要一点工作。

我希望在我自定义的SwipeRefreshLayout中,用xml的属性来指定一个id,然后将这个指定的id加载到mScrollableChild上。

这方面的相关知识点是:AttributeSet、TypedArray。我也是刚刚抱佛脚解决的,不由感慨基础很重要。

在values文件夹中新建一个attrs.xml,内容如下:

在使用自定义View中指定ListView的id:

最后是我的ImprovedSwipeLayout全部代码:



  1. 嘿,我在 v2ex 上看到你的帖子,想问一下能不能聊一下一起共事的可能。我现在负责的项目,设计 iOS 和 Android,虽然我本人更喜欢 iOS,但是也非常喜欢尊敬 Android 的设计。希望在这个项目中使用 Material Design 和国内形势的权衡之后的解决方案。虽然 Material Design 非常好,但是对于国内生态来说其实并不适合。我的邮箱:fradser#gmail.com,QQ 是 524026510。希望能聊一下,公司是阿狸家。

  2. 今天花了五十块钱剪了一个很抽象的发型。回公司后,一同事意味深长地说:“你这发型性价比挺高啊!花五十块剪了个二百五的头。”