不知道大家看源码了没有,其主要是利用嵌套滚动,而且代码相对难理解一些。
最近看到作者的这篇文章,作者抛弃了嵌套滚动,通过自定义 ViewGroup 的方式实现,相对来说,代码好理解太多了,我也实际体验了一下,体验效果还可以。
仔细看了下源码,作者的思路还是很值得学习的,一开始我以为是默认让内部空间全部展开都失去滚动效果,交给外层,实际看了代码之后发现并没有抛弃 RecyclerView 这些复用,他在其外层 scrollTo的时候,会考虑交给内层控件滚动,感兴趣可以学习一波。
概述
ConsecutiveScrollerLayout是我在GitHub开源的一个Android自定义滑动布局,它可以让多个滑动布局和普通控件在界面上像一个整体一样连续顺畅地滑动。
试想我们有这样一个需求,在一个界面上有轮播图、像九宫格一样的分类布局、几个样式不一样的列表,中间还夹杂着各种广告图和展示各类活动的布局,这样的设计在大型的app首页上非常常见。
又比如像咨询类的文章详情页或者电商类的商品详情页这种一个WebView加上原生的评论列表、推荐列表和广告位。这种复杂的布局实现起来往往比较困难,而且对于页面的滑动流畅性和布局的显示效率要求较高。
在以前我遇到这种复杂的布局,会使用我在Github开源的项目GroupedRecyclerViewAdapter 实现。
https://github.com/donkingliang/GroupedRecyclerViewAdapter
当初设计GroupedRecyclerViewAdapter,是为了能让RecyclerView方便地实现二级列表、分组列表和在一个RecyclerView上显示不同的列表。
由于GroupedRecyclerViewAdapter支持设置不同item类型的头部、尾部和子项,所有它能在一个RecyclerView上显示多种不同的布局和列表,也符合实现复杂布局的需求。
但是由于GroupedRecyclerViewAdapter并非为这种复杂布局设计的,用它来实现这种布局,需要使用者在GroupedRecyclerViewAdapter的子类上管理好页面的数据和各种类型布局的显示逻辑,显得臃肿又麻烦。
如果不把它整合在一个RecyclerView上,而是使用布局的嵌套实现,不仅严重影响布局的性能,而且解决滑动冲突也是个令人头疼的问题。尽管Google为了更好地解决滑动布局间的滑动冲突问题,在Android 5.0的时候推出了NestedScrolling机制,不过要自己来处理各种滑动问题,依然不是一件容易的事情。
无论多么复杂的页面,它都是由一个个小控件组成的。如果能有一个布局容器帮我们处理好布局内所有的子View的滑动问题,使得无论是普通控件还是滑动布局,在这个容器里都能像一个整体一样滑动,滑动它就好像是滑动一个普通的ScrollView一样。那么我们是否就可以不用再关心布局的滑动冲突和滑动性能问题。
无论多么复杂的布局,我们都只需要考虑布局的各个小部分该用什么控件就用什么控件,任何复杂的布局都将不再复杂。
ConsecutiveScrollerLayout正是基于这样的需求而设计的。
设计思路
在构思ConsecutiveScrollerLayout时,我是考虑使用NestedScrolling机制实现的,但是后来我放弃了这种方案,主要原因有二:
1、NestedScrolling机制主要是协调父布局和子布局的滑动冲突,分发滑动事件,至于布局的滑动是由它们自己各自完成的。
这不符合我希望把ConsecutiveScrollerLayout的所有子View当作一个滑动整体的构思,我希望把子View的内容视作是ConsecutiveScrollerLayout内容的一部分,无论是ConsecutiveScrollerLayout自身还是它的子View,都由ConsecutiveScrollerLayout来统一处理滑动事件。
2、NestedScrolling机制要求父布局实现NestedScrollingParent接口,所有可滑动的子View实现NestedScrollingChild接口。
而我希望ConsecutiveScrollerLayout在使用上尽可能的没有限制,任何View放进它都可以很好的工作,而且子View无需关心它是怎么滑动的。
否决了NestedScrolling机制后,我尝试从View的内容滑动的相关方法来寻找突破点。我发现Android几乎所有的View都是通过scrollBy() -> scrollTo()方法滑动View的内容,而且大部分的滑动布局也是直接或者间接调用这个方法来实现滑动的。
所以这两个方法是处理布局滑动的入口,通过重写这两个方法可以重新定义布局的滑动逻辑。
具体的思路是通过拦截可滑动的子view的滑动事件,使它无法自己滑动,而把事件统一交由ConsecutiveScrollerLayout处理,ConsecutiveScrollerLayout重写scrollBy()、scrollTo()方法,在scrollTo()方法中通过计算分发滑动的偏移量,决定是由自身还是具体的子View消费滑动的距离,调用自身的super.scrollTo()和子View的scrollBy()来滑动自身和子View的内容。
说了这么多,下面让我们通过代码,分析一下ConsecutiveScrollerLayout是如何实现的。下面给出的代码是源码的一些主要片段,删除了一些与设计思路和主要流程无关的处理细节,便于大家更好的理解它的设计和实现原理。
效果图
在开始前,先让大家看一下ConsecutiveScrollerLayout实现的效果。