1. 程式人生 > 其它 >終於來了:Android端個人中心頁面滑動衝突優化方案

終於來了:Android端個人中心頁面滑動衝突優化方案

背景

抖音首頁右滑可進入“個人中心”頁面,對於首頁日活上億的 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 相關原始碼:

核心攔截邏輯:

  1. 如果橫向上有可滑動的子 View ,就不攔截,讓子 View 去處理
  2. 橫向的滑動超過臨界值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技術。
對文章有何見解,或者有何技術問題,歡迎在評論區一起留言討論!