分类: 未分类

深度定制 RecyclerView 的几个例子

最近手头上接连接了几个比较复杂的需求。一个,是 github 上 star 数较多的 RecyclerViewPager,我去年深度定制了它,将 RecyclerView.Adapter 作为 ViewModel,内嵌 RecyclerView 在 ViewModel 之中,增加生命周期管理和 Utils注入,使之能够成为一个通用组件。然而,UED 要求的滑动效果,比起正常的 RecyclerView 的滑动距离,要多出半个 Item 的 Width。

考虑到 RecyclerViewPager 自身为了让 RecyclerView 的能够实现类 ViewPager 的效果,它自身就需要做一个触发滑动 => 自发滑动 => 惯性停止的事情,因此就去看 RecyclerViewPager 的自身实现。最终发现,RecyclerView 是通过 LayoutManager 来控制滑动、显示、隐藏等一系列逻辑的,其中就有 LayoutManager 的内部类 SmoothScroller 的 startSmoothScroll 方法,用来处理平滑滚动。hook 此方法,即可实现多滚动一段距离的功能。代码如下所示:

可以看到逻辑非常简单,只是判断了一下滑动方向,对于 dx 做了加减半个 item width 的功能。此外,就是看了一下 Action 这个内部类的结构,发现,如果对于不会触发滑动的 Action,会标示 onChanged field,遗憾的是 private,外部拿不到。那再 debug,发现不触发的情况下,duration 也会为一个负值。那就用 getDuration() 替代 onChanged 作为判断条件。细想也很自然,如果动作无需触发,也没有必要为动作设置持续时间 duration 了。

然而,以上简单的修改花了我两天的工作量,因为难度的本质在于,找到 hook 的地方,知道这么改会发生什么情况。那么就需要去读 Android 的源代码,看 LayoutManager 中的 SmoothScroller 是干什么用的,有哪些方法,哪个方法是滑动的关键方法。

同理,第二个案例是,如果使用横向 RecyclerView 代替 ViewPager 会有一个问题。RecyclerView 一次滑动是可以滑好几个 Item 的,但 ViewPager 始终只能一次滑一个。那么如何让 RecyclerView 实现 ViewPager 的滑动效果呢?

分析得知,滑动分成两种,滑动和甩动,前者针对于手指在屏幕上实际的触摸滑动距离,后者则是触摸滑动的速度,由速度计算出惯性滑动需要的距离。速度的处理在 fling 函数之中,因此,实现效果的代码如下:

这样就阻断了 super.fling() 中对甩动速度的处理。

推荐一个搜索 Android 源码的方法,google 关键词 + site:android.googlesource.com,这里带了大量的源码,相比使用 IDE 更为快捷。