為什麼我不用ViewPager或RecyclerView來做上下滑切換

SCNUzy發表於2019-05-07

上下滑切換翻頁大概是這樣的效果:

SlidableLayout

目前網上有諸多如 “仿抖音上下滑...” “仿花椒映客直播...” 之類的技術分享,都有講述實現上下滑切換頁面的方案,其中以 ViewPagerRecyclerView + SnapHelper 兩種方案為多,但是都有明顯的缺點。以下是一些個人的看法:

為什麼ViewPager不合適

ViewPager 自帶的滑動效果完全滿足場景,而且支援 FragmentView 等UI繫結,只要對佈局和觸控事件部分作一些修改,就可以把橫向的 ViewPager 改成豎向。

但是沒有複用是個最致命的問題。在 onLayout 方法中,所有子View會例項化並一字排開在佈局上。當Item數量很大時,將會是很大的效能浪費。

其次是可見性判斷的問題。很多人會以為 FragmentonResume 的時候就是可見的,而 ViewPager 中的 Fragment 就是個反例,尤其是多個 ViewPager 巢狀時,會同時有多個父 Fragment 多個子 Fragment 處於 onResume 的狀態,卻只有其中一個是可見的。除非放棄 ViewPager 的預載入機制。在頁面內容曝光等重要的資料上報時,就需要判斷很多條件:onResumedsetUserVisibleHintsetOnPageChangeListener 等。

最後是巢狀滑動的問題。同向巢狀滑動是很常見的場景,Google 新出的滑動佈局基本都使用 NestedScrolling 機制來解決巢狀滑動。但是 ViewPager 依然需要開發者自己來處理複雜的滑動衝突。

為什麼RecyclerView不合適

RecyclerView + SnapHelper 的方案比 ViewPager 好得多,既有對 View 的複用,滑動事件也已經處理好。

但是依然無法雙向無限滑動。我們可以在 getItemCount 方法中返回 Integer.MAX_VALUE 來假裝無限個滑動元素。但是為了從頭開始就可以下拉滑到上一個,元素列表的索引就不能初始化為0,那初始值為 Integer.MAX_VALUE/2 ? 無論怎麼掩飾,理論上還是有滑動到頭的一天。

更優的一種解決方案

使用兩個 View 輪流切換就能完成上下滑的場景。這種方案也有APP在用,但是網上幾乎找不到原始碼。因此我把它抽成獨立的庫放在Github倉庫:致力於打造通用、易用和流暢的上下滑動翻頁佈局SlidableLayout

SlidableLayout 本質是一個包含兩個相同大小子 ViewFrameLayout 。兩個子 View 分別作為 TopViewBackView

靜止狀態下,使用者只會看見 TopView ,而 BackView 被移除或隱藏。

手指向上拖動時, TopView 在y軸上向上偏移, BackView 開始出現,而且 BackView 的頂部與 TopView 的底部相接。

手指向上拖動一定距離後放手,TopView 繼續在y軸上做動畫直到完全消失, BackView 向上直到完全出現。然後 TopViewBackView 互換身份,原來的 BackView 成為現在的 TopView ,原來的 TopView 被移除或隱藏,成為下一次滑動的 BackView 。互換後完成一次滑動。

反之,手指向下滑動亦然。

同時要考慮手指放手後,滑動距離不夠或者速度不夠時,TopView 會沿著y軸回彈到原來的位置。 BackView 也跟著原路返回,直到被移除或隱藏。

SlidableLayout 還實現了 NestedScrollingChild 介面,使其能夠與自定義的下拉重新整理佈局巢狀滑動。

原始碼和使用例子參照 github.com/YvesCheung/… 。如有不同意的地方,請在 Github 留下 Issue

相關文章