現在市面的應用界面大多是通過一個Fragment容器+底部導航欄框架來實現頁面切換的,而當我們想要去搭建一個這樣的框架時,上層的Fragment容器是可選的,常見的有FragmentContanerView、ViewPager、ViewPager2。我們應該如何選擇呢?這時就需要考慮這三者的自身自帶 ...
現在市面的應用界面大多是通過一個Fragment容器+底部導航欄框架來實現頁面切換的,而當我們想要去搭建一個這樣的框架時,上層的Fragment容器是可選的,常見的有FragmentContanerView、ViewPager、ViewPager2。我們應該如何選擇呢?這時就需要考慮這三者的自身自帶的一些特性區別:
在我目前寫的項目中這三者都有使用到,一開始並沒有覺得有什麼區別,因為寫的都是一些很簡單的項目。但在一次使用FragmentContainerView的過程中,我明顯的感覺到了其與ViewPager2的一個顯著區別:切換Fragment時生命周期方法的調用。
在這個使用FragmentContainerView+BottomNavigationView的項目中,我在其中一個頁面加入了自定義的自動輪播圖控制項,而當我通過底部導航欄切換到另一界面再切回來時,我發現我的輪播圖控制項總是會自動從第一張圖開始載入,而在我之前使用ViewPager2+BottomNaviagtionView的項目中我也使用了自動輪播圖控制項,但是並沒有出現這個問題:即通過底部導航欄切換到另一個頁面再切回來並不會使輪播圖控制項重新輪播。
我開始思考:是什麼導致了自動輪播圖控制項每次切回來都會從頭開始輪播?
以下是我的排查過程:
一開始,我懷疑我的輪播圖自動輪播的邏輯寫的有問題,返回查看,發現與另一個項目的是一樣的,那就可以排除這個原因
進而,我開始思考輪播圖的載入時機。我的輪播圖數據是網路數據,而請求輪播圖網路數據是在Fragment的onViewCreated()生命周期方法中執行的。也就是說與Fragment的生命周期方法調用時機有關。
1.FragmentContainerView+BottomNavigationView切換Fragment時的生命周期調用情況:
我開始在Fragment的各個生命周期方法中打Log,然後發現,當我通過底部導航欄切換FragmentContainerView中的fragment時,被切換的Fragment除了首頁的Fragment只會立即執行onPause()->onStop()->onDestroyView(),其他的都會立即執行onPause()->onStop()->onDestroyView(onDestory(),當我再切換回來之後,會執行onCreatedView()->onViewCreated()。也就是說每當我切換回去之後輪播圖都會再重新請求一次數據,從而導致了每次切換回輪播圖界面輪播圖都會重新開始輪播。
首頁Fragment被切換後的生命周期調用情況
重新切迴首頁Fragment後的生命周期調用情況
其他Fragment被切換後的生命周期調用情況
重新切回其他Fragment後的生命周期調用情況
2.ViewPager2+BottomNavigationView切換Fragment的生命周期調用情況:
而後,我又返回去查看另一個項目(即使用ViewPager2+BottomNavigationView的)的界面切換導致的Fragment生命周期方法的變化。發現:當通過BottomNavigationView切換fragment時,被切換的fragment只會執行onPause(),但並不會立即執行onStop(),而是等待一段時間後或當用戶退出應用才會調用onStop()方法;且當切回去時,也不會重新執行onCreatedView()->onViewCreated(),onCreatedView()->onViewCreated()只有當Fragment首次被展示時才會調用。故使用ViewPager2的項目中切換Fragment不會導致輪播圖數據重新載入而導致輪播圖重新輪播。
Fragment被切換後生命周期調用情況
Fragment被切回來後生命周期調用情況
3.ViewPager+BottomNaviagtionView切換Fragment的生命周期調用情況:
而後,我又研究了一下ViewPager+BottomNaviagtionView切換fragment的生命周期調用情況。ViewPager有預載入功能,比較複雜一點
同樣是只有三個頁面(Title,Leaderboard,Register),由BottomNavigationView控制切換:
初次載入時,ViewPager會預載入其臨近的一個Fragment,在這個例子中,就是顯示首頁Title頁面時也預載入了Leaderboard頁面,生命周期調用如下:
當我從Title切換到臨近的Leaderboard頁面時,被切換的Title頁面的生命周期沒有任何變化,切換後的Leaderboard頁面也沒有任何變化(因為其已經被預載入過了),而此時,與Leaderboard臨近的Register頁面也會被預載入,生命周期調用如下:
而當我從Leaderboard頁面切換到第三個頁面Register時,Leaderboard頁面和Register頁面的生命周期都不會發生變化,但第一個頁面Title會被銷毀,生命周期調用如下:
同樣的,當我從第三個頁面Register切換到第二個頁面Leaderboard時,第一個頁面Title又會被重新載入,生命周期調用如下:
當我再從第二個頁面Leaderboard切換到第一個界面時,第三個頁面Register又會被銷毀,生命周期方法調用如下:
由上述測試我們可以看到ViewPager的預載入機制和頁面銷毀機制,且當界面上只有三個頁面時,除非用戶退出界面,否則第二個頁面是永遠不會銷毀的。
4.總結:
(1)FragmentContainerView+BottomNavigationView:
既沒有預載入機制也沒有緩存機制,每切換一次,被切換的Fragment就會被銷毀,下次切回來要重新創建View:onCreatedView()->onViewCreated()
(2)ViewPager2+BottomNavigationView:
預設是沒有預載入機制的,除非我們手動調用viewPager.offscreenPageLimit 屬性進行設置,否則預設是沒有預載入(其預載入與ViewPager的預載入機制還不太一樣,預載入的界面不會調用onResume()渲染界面,而ViewPager的預載入界面是直接渲染好的),但是預設有緩存機制,當頁面被載入到界面上後不會銷毀(除非用戶退出界面),只會暫停onPause,再次回來時直接onResume()
(3)ViewPager+BottomNavigationView:
預設有預載入機制,會預載入臨近的左右頁面。也有預設的緩存機制,但與ViewPager2的不同,當從本頁面切換到間隔一個的頁面,本頁面就會被銷毀(如從1切換到3時,1就會被銷毀)。