抖音Android端个人中心页面滑动冲突优化方案
背景
抖音首页右滑可进入“个人中心”页面,对于首页日活上亿的 APP 来说,这个页面的pv理论上应该不会太小。但是某些时候在此页面会出现滑动冲突的小问题,不太利于用户体验,通过反复的把玩测试,找到了必现的操作,作为一个资深的抖迷和一个非资深的 Android 开发的我,产生了钻牛角尖的想法—想看看问题是怎么产生的,以及有没有可优化的方案。
问题现象
首页右滑可进入“个人中心”页面,然后在底部的 RecylerView 上先左右滑动,但是不触发它们父布局 ViewPager 的切换,然后手指不抬起,进行上下滑动,此时 RecylerView 会接收滑动事件,导致滑动错位,如下图所示:
原因分析
问题明确了,接下来就是分析是如何产生的了。我通过综合分析发现,抖音用的是自定义 LinearLayout 的方式来布局 header + Viewpager + RecyclerView 的,进而通过拦截 LinearLayout 的 disptachTouchEvent
来处理的嵌套滑动。整体的滑动流程如图所示:
- 当手指触摸屏幕时,记录位置,滑动后,判断是横向竖向,只判断一次
- 如果是上下滑动,则判断是触发最外层 LinearLayout 的滑动,还是触发 RecyclerView 的自身滑动。
- 触发自身的滑动就是调用自己的
scrollBy(0,dy)
,注意 此时的事件还是会往下传递到 RecyclerView ,但是由于相对于 RecyclerView 自身来说滑动差值很小,视觉上可忽略。 - 不触发自身的滑动就会直接分发下去,此时 RecyclerView 自身来说竖向(
dy
)差值变化较大,正常滑动。 - 出现问题时,用户的手先触发左右滑动,这时候由于 RecyclerView 父布局 ViewPager 中的一些临界判断没被触发,所以没拦截事件,事件还是到了 RecyclerView 中,此时如果再次上下滑动,由于1中的判断单次滑动周期内只触发了一次,还被认为是左右滑动事件,所以 LinearLayout 布局本身没有滚动,但是 RecyclerView 正常响应滚动,导致的出现滑动偏差。
优化方案
问题分析的差不多了,其实本来也就结束了,但是惊喜的发现原来这个自定义的滑动布局是扩展自开源库:https://github.com/cpoopc/ScrollableLayout
但是已经长时间没人维护了。不过通过这个原始的库。可以看到核心逻辑还是一致的,经过调试编译发现,确实这个库也同样存在这个问题,那就基于此库着手试着解决一下吧。
开源库的原本代码:
根据分析就是在图中 else
中其实又触发了上下滑动逻辑,而外层的自定义 LinearLayout 布局没有跟随滑动导致的。那我们是不是可以在里面加个判断,除去真正的左右滑动逻辑(ViewPager事件),剩下的事件就是触发 RecylcerView 滑动的了(相当于过滤了横向的,留下的竖向的),我们再次判断外层的自定义 LinearLayout 布局是否需要联动,如果需要再次联动就好了。
站在巨人肩膀上,系统控件的处理一般都可以借鉴,源码之下,一切清晰,横向的可以参考 ViewPager 的事件拦截,竖向的可以参考 RecyclerView 的事件处理逻辑。分析两个控件的 onIntercepetTouchEvent()
, 拿到其核心的判断是否响应滑动的逻辑为我们所用。
ViewPager 相关源码:
[图片上传失败...(image-8de366-1658990435963)]
核心拦截逻辑:
- 如果横向上有可滑动的子 View ,就不拦截,让子 View 去处理
- 横向的滑动超过临界值
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性能优化项目实战》(内含腾讯、字节跳动、阿里巴巴、百度、网易、美团、谷歌、爱奇艺等等团队)
腾讯团队
- 腾讯光影研究室—Android P之Smart Linkify
- 腾讯Bugly—动态下发 so 库在 Android APK 安装包瘦身方面的应用
- QQ音乐—彻底弄懂浏览器缓存策略
- QQ音乐Android编译提速之路
- 全民k歌适配arm64-v8a方案
- 全民K歌内存篇1——线上监控与综合治理
- 全民K歌内存篇2——虚拟内存浅析
- 全民K歌内存篇3——native内存分析与监控
- 腾讯Bugly—对字符串匹配算法的一点理解
字节团队
- 字节跳动技术团队— 深入理解Gradle框架之一:Plugin,Extension, buildSrc
- 字节跳动技术团队—深入理解gradle框架之二:依赖实现分析
- 字节跳动技术团队—Scene:Android 开源页面导航和组合框架
- 字节跳动技术团队—AwCookieManager.nativeGetCookiecrash 排查
- 字节跳动技术团队—另类 BadTokenException 问题分析和解决
- 字节跳动技术团队—抖音包大小优化-资源优化
- 字节跳动技术团队—二维码扫描优化
- 字节跳动—Android Camera内存问题剖析
- 抖音BoostMultiDex优化实践:Android低版本上APP首次启动时间减少80%
- 抖音BoostMultiDex优化实践:Android低版本上APP首次启动时间减少80%(二)
- 抖音 Android 性能优化系列:Java 内存优化篇
- 今日头条 Android ‘秒’ 级编译速度优化
阿里团队
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 支付宝 App 构建优化解析:通过安装包重排布优化 Android 端启动性能
- 支付宝 App 构建优化解析:Android 包大小极致压缩
- 解决支付宝包体积优化的遗留问题:运行时获取dexpc
- 闲鱼技术—曾梦想 if-else 走天涯?看看“责任树模式”优化
- 闲鱼如何在2个月内实现Android启动速度翻倍的?
- 高德技术—Android Native 内存泄漏系统化解决方案
- 天猫精灵技术—史上最全Android渲染机制讲解(长文源码深度剖析)
百度团队
- 百度APP-Android H5首屏优化实践
- 百度App技术—一种简单优雅的TextView行间距适配方案
- 百度App技术—Android 10分区存储介绍及百度APP适配实践
- 百度App技术—Gradle 与 Android 构建入门
- 百度App组件化之路
- 百度App网络深度优化系列《三》弱网优化
网易团队
- 网易新闻客户端 H5 秒开优化
- 网易新闻构建优化:如何让你的构建速度“势如闪电”
- 网易传媒技术团队—AOP技术在客户端的应用与实践
- 网易大数据|互联网产品决策秘笈: AB测试
美团团队
- 美团技术团队—Android静态代码扫描效率优化与实践
- 美团技术团队—Probe:Android线上OOM问题定位组件
- 美团技术团队—移动端UI一致性解决方案
- 美团—设计稿(UI视图)自动生成代码方案的探索
- …
爱奇艺团队
- 爱奇艺技术产品团队—干货|安卓APP崩溃捕获方案——xCrash
- Android篇 | 爱奇艺App启动优化实践分享
- 二维码扫描优化及爱奇艺App的实践
谷歌团队
- 谷歌开发者—Flutter 您需要知道的知识点
- 谷歌—协程中的取消和异常 | 异常处理详解
- …
京东团队
- 京东技术—任意URL跳转漏洞修复与JDK中getHost()方法之间的坑
- 京东—LayoutInflater原理分析与复杂布局优化实践
- …
其他
- 花椒Android端自动化测试实践
- 开源 | BoostMultiDex:挽救 Android Dalvik 机型APP升级安装体验
- 有赞移动Crash平台建设
- 贝壳APP Top Experience系列 | Android方法耗时统计工具
- 一种按照library的维度进行Android包大小分析的方法和实践
- …
文章篇幅有限,需要《2022最新一线互联网公司Android性能优化项目实战》的朋友,可点击【公众号】免费领取!