抖音Android端“个人中心”页滑动冲突优化方案

4,631 阅读5分钟

背景

抖音首页右滑可进入“个人中心”页面,对于首页日活上亿的APP来说,这个页面的pv理论上应该不会太小。但是某些时候在此页面会出现滑动冲突的小问题,不太利于用户体验,通过反复的把玩测试,找到了必现的操作,作为一个资深的抖迷和一个非资深的Android开发的我,产生了钻牛角尖的想法—想看看问题是怎么产生的,以及有没有可优化的方案。

场景

首页右滑可进入“个人中心”页面,然后在底部的RecylerView上先左右滑动,但是不触发它们父布局ViewPager的切换,然后手指不抬起,进行上下滑动,此时RecylerView会接收滑动事件,导致滑动错位(具体的可见视频,这里随机的选了抖音的Andorid端版本,分别是v14.8.0、v14.9.0、v1.7.2、v3.9.0。发现问题都存在…… 同时用iOS端测试时均无此问题)。

                           

分析

问题明确了,接下来就是分析是如何产生的了。我这边通过综合分析发现,抖音这边用的是自定义LinearLayout的方式来布局header + Viewpager + RecyclerView的,进而通过拦截LinearLayout的disptachTouchEvent 来处理的嵌套滑动。整体的滑动流程如图所示:

  1. 当手指触摸屏幕时,记录位置,滑动后,判断是横向竖向,只判断一次

  2. 如果是上下滑动,则判断是触发最外层LinearLayout的滑动,还是触发RecyclerView的自身滑动。

  3. 触发自身的滑动就是调用自己的scrollBy(0,dy),注意 此时的事件还是会往下传递到 RecyclerView,但是由于相对于RecyclerView自身来说滑动差值很小,视觉上可忽略。

  4. 不触发自身的滑动就会直接分发下去,此时RecyclerView自身来说竖向(dy)差值变化较大,正常滑动。

  5. 出现问题时,用户的手先触发左右滑动,这时候由于RecyclerView父布局ViewPager中的一些临界判断没被触发,所以没拦截事件,事件还是到了RecyclerView中,此时如果再次上下滑动,由于1中的判断单次滑动周期内只触发了一次,还被认为是左右滑动事件,所以LinearLayout布局本身没有滚动,但是RecyclerView正常响应滚动,导致的出现滑动偏差。

优化方案

问题分析的差不多了,其实本来也就结束了,但是惊喜的发现原来这个自定义的滑动布局是扩展自开源库:github.com/cpoopc/Scro… 但是已经长时间没人维护了。不过通过这个原始的库。可以看到核心逻辑还是一致的,经过调试编译发现,确实这个库也同样存在这个问题,那就基于此库着手试着解决一下吧。

库的原来的代码如图:

根据分析就是在图中else中其实又触发了上下滑动逻辑,而外层的自定义LinearLayout布局没有跟随滑动导致的。那我们是不是可以在里面加个判断,除去真正的左右滑动逻辑(ViewPager事件),剩下的事件就是触发RecylcerView滑动的了(相当于过滤了横向的,留下的竖向的),我们再次判断外层的自定义LinearLayout布局是否需要联动,如果需要再次联动就好了。

站在巨人肩膀上,系统控件的处理一般都可以借鉴,源码之下,一切清晰,横向的可以参考ViewPager的事件拦截,竖向的可以参考RecyclerView的事件处理逻辑。分析两个控件的onIntercepetTouchEvent(),拿到其核心的判断是否响应滑动的逻辑,为我们所用。

ViewPager相关源码如图:

核心拦截逻辑:

1.如果横向上有可滑动的子View,就不拦截,让子View****去处理

2.横向的滑动超过临界值mTouchSlop**,并且大于竖向滑动距离的2倍,进行拦截**

我们需要把相关的判断代码都copy过来,然后加入到我们自定义LinearLayout中

此时进行Log调试发现还是有问题…… 原来ViewPager中判断了是否是子View消费事件**,这里我们不能照搬过来,我们要取反,即如果当前自定义的LinearLayout中有横向可滑动的View,我们的isHorizontalDrag方法应该返回true****。**

修改后的如图:

到此横向判断的过滤条件写好了。下面看竖向的RecyclerView的拦截代码,其实非常简单:

1.当竖向可滑动并且竖向的差值dy 大于临界值mTouchSlop时,即响应事件。

Copy过来加入测试:

经运行测试,发现问题确实已经解决了。

  • 简单来说,用户横向滑动时,通过增加****isHorizontalDrag() 判断是否有子View消费横向事件。

  • 有,则啥也不做

  • 没有,那么我们判断是不是要最外层的LinearLayout消费其中的竖向部分,满足条件后,自身消费事件滚动。

其他

以上是个人对于抖音Android端“个人中心”页面滑动冲突优化的拙见,优化方案,仅仅是自己做过简单测试,后续如果有计划,个人觉得可以使用谷歌的嫡系CoordinatorLayout来处理这种嵌套滑动(虽然有坑,但官方和网友大神都在填,总体来说,表现也不错……)。欢迎大家多多交流。