上下滑切換翻頁大概是這樣的效果:
目前網上有諸多如 “仿抖音上下滑...” “仿花椒映客直播...” 之類的技術分享,都有講述實現上下滑切換頁面的方案,其中以 ViewPager
和 RecyclerView
+ SnapHelper
兩種方案為多,但是都有明顯的缺點。以下是一些個人的看法:
為什么ViewPager不合適
ViewPager
自帶的滑動效果完全滿足場景,而且支持 Fragment
和 View
等UI綁定,只要對布局和觸摸事件部分作一些修改,就可以把橫向的 ViewPager
改成豎向。
但是沒有復用是個最致命的問題。在 onLayout
方法中,所有子View會實例化并一字排開在布局上。當Item數量很大時,將會是很大的性能浪費。
其次是可見性判斷的問題。很多人會以為 Fragment
在 onResume
的時候就是可見的,而 ViewPager
中的 Fragment
就是個反例,尤其是多個 ViewPager
嵌套時,會同時有多個父 Fragment
多個子 Fragment
處于 onResume
的狀態,卻只有其中一個是可見的。除非放棄 ViewPager
的預加載機制。在頁面內容曝光等重要的數據上報時,就需要判斷很多條件:onResumed
、 setUserVisibleHint
、 setOnPageChangeListener
等。
最后是嵌套滑動的問題。同向嵌套滑動是很常見的場景,Google 新出的滑動布局基本都使用 NestedScrolling 機制來解決嵌套滑動。但是 ViewPager 依然需要開發者自己來處理復雜的滑動沖突。
為什么RecyclerView不合適
RecyclerView
+ SnapHelper
的方案比 ViewPager
好得多,既有對 View
的復用,滑動事件也已經處理好。
但是依然無法雙向無限滑動。我們可以在 getItemCount
方法中返回 Integer.MAX_VALUE 來假裝無限個滑動元素。但是為了從頭開始就可以下拉滑到上一個,元素列表的索引就不能初始化為0,那初始值為 Integer.MAX_VALUE/2 ? 無論怎么掩飾,理論上還是有滑動到頭的一天。
更優的一種解決方案
使用兩個 View 輪流切換就能完成上下滑的場景。這種方案也有APP在用,但是網上幾乎找不到源碼。因此我把它抽成獨立的庫放在Github倉庫:致力于打造通用、易用和流暢的上下滑動翻頁布局SlidableLayout。
SlidableLayout 本質是一個包含兩個相同大小子 View
的 FrameLayout
。兩個子 View
分別作為 TopView 和 BackView 。
靜止狀態下,用戶只會看見 TopView ,而 BackView 被移除或隱藏。
手指向上拖動時, TopView 在y軸上向上偏移, BackView 開始出現,而且 BackView 的頂部與 TopView 的底部相接。
手指向上拖動一定距離后放手,TopView 繼續在y軸上做動畫直到完全消失, BackView 向上直到完全出現。然后 TopView 和 BackView 互換身份,原來的 BackView 成為現在的 TopView ,原來的 TopView 被移除或隱藏,成為下一次滑動的 BackView 。互換后完成一次滑動。
反之,手指向下滑動亦然。
同時要考慮手指放手后,滑動距離不夠或者速度不夠時,TopView 會沿著y軸回彈到原來的位置。 BackView 也跟著原路返回,直到被移除或隱藏。
SlidableLayout 還實現了 NestedScrollingChild 接口,使其能夠與自定義的下拉刷新布局嵌套滑動。
源碼和使用例子參照 github.com/YvesCheung/… 。如有不同意的地方,請在 Github 留下 Issue。