終於來了: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 相關原始碼:
核心攔截邏輯:
- 如果橫向上有可滑動的子 View ,就不攔截,讓子 View 去處理
- 橫向的滑動超過臨界值
mTouchSlop
,並且大於豎向滑動距離的2倍,進行攔截
我們需要把相關的判斷程式碼都 copy 過來,然後加入到我們自定義 LinearLayout 中
此時進行 Log 除錯發現還是有問題, 原來 ViewPager 中判斷了是否是子 View 消費事件,這裡我們不能照搬過來,我們要取反,即如果當前自定義的 LinearLayout 中有橫向可滑動的 View,我們的isHorizontalDrag
方法應該返回true
。
修改後的程式碼:
到此橫向判斷的過濾條件寫好了。下面看豎向的 RecyclerView 的攔截程式碼,非常簡單:
當豎向可滑動並且差值dy
大於臨界值mTouchSlop
時,即響應事件。
經執行測試發現問題已經解決。
總結
簡單來說,使用者橫向滑動時,通過增加 isHorizontalDrag() 判斷是否有子 View 消費橫向事件。如果有則啥也不做,如果沒有,那麼我們判斷是不是要最外層的 LinearLayout 消費其中的豎向部分,滿足條件後,自身消費事件滾動。
以上是個人對於抖音“個人中心”頁面滑動衝突優化的拙見及優化方案,僅僅是自己做過簡單測試,個人覺得更好的方案可以使用谷歌的嫡系 CoordinatorLayout 來處理這種巢狀滑動。
原文連結:https://juejin.cn/post/6936050349400653860
文末
您的點贊收藏就是對我最大的鼓勵!
歡迎關注我,分享Android乾貨,交流Android技術。
對文章有何見解,或者有何技術問題,歡迎在評論區一起留言討論!