自定义控件

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

2022-07-28  本文已影响0人  _Jun

背景

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

问题现象

首页右滑可进入“个人中心”页面,然后在底部的 RecylerView 上先左右滑动,但是不触发它们父布局 ViewPager 的切换,然后手指不抬起,进行上下滑动,此时 RecylerView 会接收滑动事件,导致滑动错位,如下图所示:

原因分析

问题明确了,接下来就是分析是如何产生的了。我通过综合分析发现,抖音用的是自定义 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 正常响应滚动,导致的出现滑动偏差。

优化方案

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

开源库的原本代码:

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

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

ViewPager 相关源码:

[图片上传失败...(image-8de366-1658990435963)]

核心拦截逻辑:

  1. 如果横向上有可滑动的子 View ,就不拦截,让子 View 去处理
  2. 横向的滑动超过临界值 mTouchSlop ,并且大于竖向滑动距离的2倍,进行拦截

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

[图片上传失败...(image-c1c2f8-1658990435963)]

[图片上传失败...(image-833da1-1658990435963)]

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

修改后的代码:

到此横向判断的过滤条件写好了。下面看竖向的 RecyclerView 的拦截代码,非常简单。当竖向可滑动并且差值 dy 大于临界值 mTouchSlop 时,即响应事件。

经运行测试发现问题已经解决。

总结

简单来说,用户横向滑动时,通过增加 isHorizontalDrag() 判断是否有子 View 消费横向事件。如果有则啥也不做,如果没有,那么我们判断是不是要最外层的 LinearLayout 消费其中的竖向部分,满足条件后,自身消费事件滚动。

以上是个人对于抖音“个人中心”页面滑动冲突优化的拙见及优化方案,仅仅是自己做过简单测试,个人觉得更好的方案可以使用谷歌的嫡系 CoordinatorLayout 来处理这种嵌套滑动。

最后

分享一份2022最新一线互联网公司Android性能优化项目实战(内含腾讯、字节跳动、阿里巴巴、百度、网易、美团、谷歌、爱奇艺等等团队)

腾讯团队

字节团队

阿里团队

百度团队

网易团队

美团团队

爱奇艺团队

谷歌团队

京东团队

其他

文章篇幅有限,需要《2022最新一线互联网公司Android性能优化项目实战》的朋友,可点击【公众号】免费领取!

上一篇下一篇

猜你喜欢

热点阅读