ViewPager+TabLayout+Fragment懶載入機制完全解析

橙澄發表於2017-08-22

ViewPager+TabLayout+Fragment懶載入機制完全解析

144 
作者  Ruheng 
2017.03.03 10:52*  字數 1046  閱讀 1905 評論 13

在Fragment和ViewPager配合使用的時候,ViewPager會使用預載入機制,使得我們在沒有跳轉到對應頁面時,可能已經載入好了,這是個非常不好的使用者體驗,浪費使用者的流量。本篇文章主要講解在ViewPager+TabLayout+Fragment的情況下,Fragment的懶載入機制的實現以及專案封裝情況。

一、setUserVisibleHint(boolean isVisibleToUser)

setUserVisibleHint(boolean isVisibleToUser)是Fragment中的一個回撥函式。當前Fragment可見時,setUserVisibleHint()回撥,其中isVisibleToUser=true。當前Fragment由可見到不可見或例項化時,setUserVisibleHint()回撥,其中isVisibleToUser=false。

setUserVisibleHint(boolean isVisibleToUser)呼叫時機

①在Fragment例項化,即在ViewPager中,由於ViewPager預設會預載入左右兩個頁面。此時預載入頁面回撥的生命週期流程:setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()--> onActivityCreate() --> onStart() --> onResume()

此時,setUserVisibleHint() 中的引數為false,因為不可見。

②在Fragmetn可見時,即ViewPager中滑動到當前頁面時,因為已經預載入過了,之前生命週期已經走到onResume() ,所以現在只會回撥:setUserVisibleHint() 

此時,setUserVisibleHint() 中的引數為true,因為可見。

③在Fragment由可見變為不可見,即ViewPager由當前頁面滑動到另一個頁面,因為還要保持當前頁面的預載入過程,所以只會回撥:setUserVisibleHint()

此時,setUserVisibleHint() 中的引數為false,因為不可見。

④由TabLayout直接跳轉到一個未預載入的頁面,此時生命週期的回撥過程:setUserVisibleHint() -->setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()--> onActivityCreate() --> onStart()
--> onResume()

回撥了兩次setUserVisibleHint() ,一次代表初始化時,傳入引數是false,一次代表可見時,傳入引數是true。這種情況比較特殊。

總結:無論何時,setUserVisibleHint()都是先於其他生命週期的呼叫,並且初始化時呼叫,可見時呼叫,由可見轉換成不可見時呼叫,一共三次時機。

二、封裝實現懶載入機制

載入網路的情況:
①setUserVisibleHint()中引數為true,即Fragment可見;

②onCreateView()方法呼叫完畢,返回rootView,防止造成空指標問題;

③第一次呼叫網路載入,之前沒載入過,即isFirst = false(有些彆扭,可以改成true,對應程式碼做下修改)。

滿足以上條件,載入網路請求。

取消載入網路的情況:

①setUserVisibleHint()中引數由true變為false,即Fragment由可見變為不可見;

②不是第一次呼叫網路載入,之前載入過,即isFirst = true。

滿足以上兩者條件(只滿足第一點也可以),取消網路請求。

封裝程式碼

在BaseFragment中進行封裝

其中isFragment表示當前Fragment的可見情況
isFirst表示是否已經載入過
onFragmentVisibleChange()是一個可繼承的空方法,子類實現,傳入true,代表載入網路,傳入false,代表取消網路載入。

 @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            isFragmentVisible = true;
        }
        if (rootView == null) {
            return;
        }
        //可見,並且沒有載入過
        if (!isFirst&&isFragmentVisible) {
            onFragmentVisibleChange(true);
            return;
        }
        //由可見——>不可見 已經載入過
        if (isFragmentVisible) {
            onFragmentVisibleChange(false);
            isFragmentVisible = false;
        }
    }
    protected void onFragmentVisibleChange(boolean isVisible) {

    }

其中有一種特殊情況不能滿足上述程式碼中的條件進行網路載入。

特殊情況:當TabLayout跳轉到一個沒有預載入過的Fragment,連續呼叫兩次setUserVisibleHint方法,但此時rootView為空,不能進行載入。需要在onCreateView()方法最後判斷是否進行網路載入,然後呼叫onFragmentVisibleChange(true)方法

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     .................
        //可見,但是並沒有載入過
        if (isFragmentVisible && !isFirst) {
            onFragmentVisibleChange(true);
        }
        return rootView;
    }

子類實現的例項:

@Override
    protected void onFragmentVisibleChange(boolean isVisible) {
        if(isVisible){
            //可見,並且是第一次載入
            mPresenter.requestPhotoList(SIZE,mStartPage);
            LoadingDialog.showDialogForLoading(getActivity());
        }else{
            //取消載入
            RxDisposeManager.get().cancel("photoList");
            LoadingDialog.cancelDialogForLoading();
        }
    }

三、效果實現



上圖效果可以看出實現了懶載入,並且再次滑動回對應Fragment,也不會載入,通過下拉重新整理才能更新資料,減少流量的耗費。


上圖效果可以看出,當TabLayout直接跳轉到沒有預載入的頁面,也可以實現懶載入,效果一樣。

小夥伴們有沒有很心動,下面有原始碼,歡迎參考使用!

原始碼地址

只需要看BaseFragment部分即可

相關文章