首發公衆號: 黑客五六七
做者: 賢榆的榆
若是喜歡,請 關注、讚揚、點在看
閱讀時間:1738字 6分鐘
Fragment的懶加載,一般都是在一個Activity中經過ViewPager管理了多個Fragment界面時,會用到的一種模式。當咱們每一個Fragment都很複雜的時候,爲了保證整個Activity的流暢度,咱們一般會將第一個Fragment先加載出來,後面的Fragment在其可見時再加載。每當咱們想到Fragment懶加載的時候一般都是使用setUserVisiable()方法配合onViewCreated方法來作懶加載,可是你會發現當使用ViewPager2+Fragment的時候,它就並不起做用了。因爲種種的歷史緣由,如今的Fragment嵌套的方式比較多了,因此這裏研究一下這些嵌套方式,看看是否可以找到一個合適方式懶加載方式做爲我這個BaseLazyFragment的實現,同時兼容這幾種嵌套方式。git
固然,個人出發點是還是從生命週期開始探索。這裏先溫故一下Fragment的生命週期吧:
爲了可以更好的觀察幾種嵌套方式在滑動過程當中的引用,除了上面的生命週期以外,我還在日誌中打印了onViewCreated和setUserVisibleHint()方法的調用時機。下面看一下個人日誌代碼:github
class LifeCycleFragment : Fragment() { companion object { fun create(position: Int): LifeCycleFragment { val fragment = LifeCycleFragment() fragment.arguments = bundleOf(Pair("p", position)) return fragment } } var position: String = "" override fun onAttach(context: Context) { position = arguments?.getInt("p").toString() super.onAttach(context) Log.d("ResumeOnly", "Fragment${position}onAttach") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d("ResumeOnly", "Fragment${position}onCreate") } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { Log.d("ResumeOnly", "Fragment${position}onCreateView") return TextView(activity).apply { text = position layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) textSize = 100f typeface = Typeface.DEFAULT_BOLD gravity = Gravity.CENTER } } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) Log.d("ResumeOnly", "Fragment${position}onActivityCreated") } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Log.d("ResumeOnly", "Fragment${position}onViewCreated") } override fun setUserVisibleHint(isVisibleToUser: Boolean) { super.setUserVisibleHint(isVisibleToUser) Log.d("ResumeOnly", "Fragment${position}isVisibleToUser$isVisibleToUser") } override fun onStart() { super.onStart() Log.d("ResumeOnly", "Fragment${position}onStart") } override fun onResume() { super.onResume() Log.d("ResumeOnly", "Fragment${position}onResume") } override fun onPause() { super.onPause() Log.d("ResumeOnly", "Fragment${position}onPause") } override fun onStop() { super.onStop() Log.d("ResumeOnly", "Fragment${position}onStop") } override fun onDestroyView() { super.onDestroyView() Log.d("ResumeOnly", "Fragment${position}onDestroyView") } override fun onDestroy() { super.onDestroy() Log.d("ResumeOnly", "Fragment${position}onDestroy") } }
經過上面的Log日誌,其實不難看出經過ViewPager+FragmentPagerAdapter(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)進行管理Fragment,在默認沒有設置offscreenPageLimit的值時,也會幫咱們多預加載一個,而預加載的這個是走到了onStart方法,當左滑時,原來預加載的這個會走onResume,同時再預加載下一個。很明顯咱們的lazyLoad方法能夠放到onResume去作。當咱們滑動到當前頁的時候走當前頁的onResume方法而後再onResume中調用lazyLoad方法。app
根據上面的日誌能夠看到,再進入當前頁面的時候和第一種同樣,在默認沒有設置offscreenPageLimit的值時,也會幫咱們多預加載一個Fragment,但不一樣的時候,兩個Fragment的生命週期都會直接走到onResume的生命週期,而且它們都多了一個isVisibleToUser布爾值來控制Fragment是否可見。ide
根據上面的log能夠看到,進入界面時它只會初始化當前的Activity,它的可見與否也是由onResume和onPause進行回調的。它的生命週期就和第一種很像了,只是在沒有設置offscreenPageLimit的狀況下,它不會去預加載下一個的生命週期。因此咱們仍然能夠經過onResume來控制懶加載。佈局
經過上面的三種嵌套的日誌。咱們只須要最後肯定一件事:就是mUserVisibleHint的默認值
google
如咱們所料,至此咱們只須要在onResume和setUserVisibleHint方法中都調用一個lazyLoad方法,並在lazyLoad中去判斷mUserVisibleHint的值以及自定義的一個loaded的值便可,另外須要注意的一點是setUserVisibleHint的方法調用的時機可能並不在Fragment的生命週期內spa
This method may be called outside of the fragment lifecycle.
and thus has no ordering guarantees with regard to fragment lifecycle method calls
因此咱們還須要判斷根佈局是否爲null,最後的實現代碼以下:日誌
abstract class BaseLazyFragment : Fragment() { private var cacheView: View? = null private var loaded: Boolean = false override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { if (cacheView == null) { cacheView = inflater.inflate(layoutId(), container, false) } return cacheView } override fun setUserVisibleHint(isVisibleToUser: Boolean) { super.setUserVisibleHint(isVisibleToUser) lazyLoad() } override fun onResume() { super.onResume() lazyLoad() } private fun lazyLoad() { if (userVisibleHint && !loaded&& cacheView != null) { initView() initData() loaded = true } } abstract fun layoutId(): Int abstract fun initData() abstract fun initView() }
最後咱們一塊兒看一下這三嵌套模式的一個懶加載實現的效果:code
這個代碼是在google的ViewPager2的代碼基礎上新增了三個頁面進行日誌打印,
代碼地址:https://github.com/luorenyu/ExplorLazyFragment
orm