上下滑切换翻页大概是这样的效果:
目前网上有诸多如 “仿抖音上下滑...” “仿花椒映客直播...” 之类的技术分享,都有讲述实现上下滑切换页面的方案,其中以 ViewPager
和 RecyclerView
+ SnapHelper
两种方案为多,但是都有明显的缺点。以下是一些个人的看法:
为什么ViewPager不合适
ViewPager
自带的滑动效果完全满足场景,而且支持 Fragment
和 View
等UI绑定,只要对布局和触摸事件部分作一些修改,就可以把横向的 ViewPager
改成竖向。
但是没有复用是个最致命的问题。在 onLayout
方法中,所有子View会实例化并一字排开在布局上。当Item数量很大时,将会是很大的性能浪费。
其次是可见性判断的问题。很多人会以为 Fragment
在 onResume
的时候就是可见的,而 ViewPager
中的 Fragment
就是个反例,尤其是多个 ViewPager
嵌套时,会同时有多个父 Fragment
多个子 Fragment
处于 onResume
的状态,却只有其中一个是可见的。除非放弃 ViewPager
的预加载机制。在页面内容曝光等重要的数据上报时,就需要判断很多条件:onResumed
、 setUserVisibleHint
、 setOnPageChangeListener
等。
最后是嵌套滑动的问题。同向嵌套滑动是很常见的场景,Google 新出的滑动布局基本都使用 NestedScrolling 机制来解决嵌套滑动。但是 ViewPager 依然需要开发者自己来处理复杂的滑动冲突。
为什么RecyclerView不合适
RecyclerView
+ SnapHelper
的方案比 ViewPager
好得多,既有对 View
的复用,滑动事件也已经处理好。
但是依然无法双向无限滑动。我们可以在 getItemCount
方法中返回 Integer.MAX_VALUE 来假装无限个滑动元素。但是为了从头开始就可以下拉滑到上一个,元素列表的索引就不能初始化为0,那初始值为 Integer.MAX_VALUE/2 ?
无论怎么掩饰,理论上还是有滑动到头的一天。
更优的一种解决方案
使用两个 View 轮流切换就能完成上下滑的场景。这种方案也有APP在用,但是网上几乎找不到源码。因此我把它抽成独立的库放在Github仓库:致力于打造通用、易用和流畅的上下滑动翻页布局SlidableLayout。
SlidableLayout 本质是一个包含两个相同大小子 View
的 FrameLayout
。两个子 View
分别作为 TopView 和 BackView 。
静止状态下,用户只会看见 TopView ,而 BackView 被移除或隐藏。
手指向上拖动时, TopView 在y轴上向上偏移, BackView 开始出现,而且 BackView 的顶部与 TopView 的底部相接。
手指向上拖动一定距离后放手,TopView 继续在y轴上做动画直到完全消失, BackView 向上直到完全出现。然后 TopView 和 BackView 互换身份,原来的 BackView 成为现在的 TopView ,原来的 TopView 被移除或隐藏,成为下一次滑动的 BackView 。互换后完成一次滑动。
反之,手指向下滑动亦然。
同时要考虑手指放手后,滑动距离不够或者速度不够时,TopView 会沿着y轴回弹到原来的位置。 BackView 也跟着原路返回,直到被移除或隐藏。
SlidableLayout 还实现了 NestedScrollingChild 接口,使其能够与自定义的下拉刷新布局嵌套滑动。
源码和使用例子参照 https://github.com/YvesCheung/SlidableLayout 。如有不同意的地方,请在 Github 留下 Issue。