Android - 懶加載

今天我們來聊一聊ViewPager+Fragment的懶加載冷尉。
參考資料:https://juejin.im/post/5adcb0e36fb9a07aa7673fbc

1.什么是懶加載涧团,為什么要用懶加載父腕?

如果我們的項(xiàng)目中使用了ViewPager+Framgment實(shí)現(xiàn)底部Tab可點(diǎn)可滑围小,那么我們都知道ViewPager有預(yù)加載功能蛤签,通過viewpager.setOffscreenPageLimit();來設(shè)置猬仁,不設(shè)置默認(rèn)加載上一個(gè)和下一個(gè)Fragment頁(yè)面事期,帶上本身也就是三個(gè)頁(yè)面(當(dāng)然如果你剛進(jìn)入就是首頁(yè),那么它會(huì)加載首頁(yè)和下一個(gè)頁(yè)面蒿偎,因?yàn)槭醉?yè)上面沒有頁(yè)面呀)朽们。預(yù)加載功能會(huì)暴露一個(gè)問題怀读,比如我剛進(jìn)入加載首頁(yè)的數(shù)據(jù),但是因?yàn)橛蓄A(yù)加載功能骑脱,那么就會(huì)執(zhí)行下一個(gè)Tab對(duì)應(yīng)的Fragmeng的生命周期菜枷,如果我下一個(gè)Tab頁(yè)數(shù)據(jù)量小還好,如果我有比較耗時(shí)的操作或者網(wǎng)絡(luò)請(qǐng)求叁丧,勢(shì)必會(huì)影響程序的性能啤誊,影響用戶的體驗(yàn)。那么我們要做的就是禁止ViewPager預(yù)加載或者提供一個(gè)只在Fragemnt可見的情況下拥娄,才去進(jìn)行耗時(shí)操作的方法蚊锹,只要Fragmeng可見我們就執(zhí)行該方法。

2.懶加載解決方式

2.1 嘗試設(shè)置setOffscreenPageLimit(失敗)

之前想既然setOffscreenPageLimit可以設(shè)置稚瘾,那我就將其設(shè)置為0牡昆,結(jié)果“然并卵”,查看源碼如下:

image.png

可以看到如果我們?cè)O(shè)置的值摊欠,小于DEFAULT_OFFSCREEN_PAGES這個(gè)常量值丢烘,那么就會(huì)被賦值為DEFAULT_OFFSCREEN_PAGES,那么我們看看DEFAULT_OFFSCREEN_PAGES值是多少:
image.png

也就是你設(shè)置的setOffscreenPageLimit要大于1才可以生效

2.2 試試懶加載

我們先來看看我們的頁(yè)面:


image.png

就是正常的viewpager+fragment些椒,每個(gè)頁(yè)面有一個(gè)TextView播瞳,可點(diǎn)可劃。三個(gè)Fragment依次是:HomeFragment,InvestFragment,UserFragment,

  • 本次要用到的生命周期的方法是:
    onCreatedView + onResume + onPause + onDestroyView

  • 本次要用到的非生命周期的方法是:setUserVisibleHint
    簡(jiǎn)單介紹一下此方法:當(dāng)fragment被用戶可見時(shí)摊沉,setUserVisibleHint()會(huì)調(diào)用且傳入true值狐史,當(dāng)fragment不被用戶可見時(shí)痒给,setUserVisibleHint()則得到false值说墨,此方法先于生命周期方法執(zhí)行

  • Fragment 主要的三個(gè)狀態(tài):第一次可見,每次可見苍柏,每次不可見

  • 對(duì)于ViewPager+Fragment使用過程中的三種情況
    (1) 使用 FragmentPagerAdapter 尼斧,F(xiàn)ragmentPagerStateAdapter不設(shè)置 setOffscreenPageLimit數(shù),采用默認(rèn)方式
    (2)使用 FragmentPagerAdapter 试吁,F(xiàn)ragmentPagerStateAdapter設(shè)置 setOffscreenPageLimit數(shù)棺棵,設(shè)置為底部Tab總數(shù)
    (3)使用 FragmentPagerAdapter ,F(xiàn)ragmentPagerStateAdapter進(jìn)入到其他頁(yè)面或者點(diǎn)擊Home鍵熄捍,返回到桌面烛恤。

其他注意的是:使用FragmentPagerAdapter 和FragmentPagerStateAdapter的區(qū)別在于FragmentPagerStateAdapter會(huì)在Fragment不可見的時(shí)候走 detach,而FragmentPagerAdapter不會(huì)余耽。

當(dāng)然我測(cè)試用的是FragmentPagerAdapter缚柏,我們先看一看正常滑動(dòng)碟贾,不設(shè)置setOffscreenPageLimit()币喧,F(xiàn)ragment生命周期是怎么走的轨域,先寫一個(gè)BaseLazyLoadFragment類繼承自Fragment.重寫我們剛才說的生命周期的方法,首頁(yè)等Fragment繼承BaseLazyLoadFragment打印生命周期(默認(rèn)進(jìn)來先加載首頁(yè)):
BaseLazyLoadFragment部分代碼如下

image.png

Fragment代碼如下
image.png

不設(shè)置setOffscreenPageLimit()杀餐,F(xiàn)ragmentPagerAdapter 結(jié)果如下


image.png

不設(shè)置setOffscreenPageLimit()干发,F(xiàn)ragmentPagerStateAdapter 結(jié)果如下:
這個(gè)結(jié)果我之前有過測(cè)試,直接告訴你們把區(qū)別是:FragmentPagerStateAdapter 會(huì)走一下兩個(gè)生命周期方法史翘,包括從切到Home頁(yè)面在返回這是一下走兩個(gè)生命周期方法枉长。

設(shè)置setOffscreenPageLimit()個(gè)數(shù)為總Tab數(shù),F(xiàn)ragmentPagerAdapter 和FragmentPagerStateAdapter結(jié)果一致琼讽,如下:

設(shè)置setOffscreenPageLimit為總數(shù)之后搀暑,一開始就加載了所有的Fragment
com.hxzk_bj_demo E/HomeFragment: isVisibleToUser------>false
com.hxzk_bj_demo E/InvestFragment:isVisibleToUser------>false
com.hxzk_bj_demo E/UserFragment: isVisibleToUser------>false
com.hxzk_bj_demo E/HomeFragment: isVisibleToUser------>true
com.hxzk_bj_demo E/HomeFragment: ------>onCreateView
com.hxzk_bj_demo E/HomeFragment: ------>onStart
com.hxzk_bj_demo E/HomeFragment: ------>onResume
com.hxzk_bj_demo E/InvestFragment: ------>onCreateView
com.hxzk_bj_demo E/UserFragment: ------>onCreateView
com.hxzk_bj_demo E/InvestFragment: ------>onStart
com.hxzk_bj_demo E/InvestFragment: ------>onResume
com.hxzk_bj_demo E/UserFragment: ------>onStart
com.hxzk_bj_demo E/UserFragment: ------>onResume
//第一個(gè)Fragment切換到第二個(gè)Fragment
com.hxzk_bj_demo E/HomeFragment: isVisibleToUser------>false
com.hxzk_bj_demo E/InvestFragment: isVisibleToUser------>true
//第二個(gè)Fragment切換到第三個(gè)Fragment
com.hxzk_bj_demo E/InvestFragment:isVisibleToUser------>false
com.hxzk_bj_demo E/UserFragment: isVisibleToUser------>true
//點(diǎn)擊Home
com.hxzk_bj_demo E/HomeFragment: ------>onPause
com.hxzk_bj_demo E/InvestFragment: ------>onPause
com.hxzk_bj_demo E/UserFragment: ------>onPause
//點(diǎn)擊Home后返回
com.hxzk_bj_demo E/HomeFragment: ------>onStart
com.hxzk_bj_demo E/InvestFragment: ------>onStart
com.hxzk_bj_demo E/UserFragment: ------>onStart
com.hxzk_bj_demo E/HomeFragment: ------>onResume
com.hxzk_bj_demo E/InvestFragment: ------>onResume
com.hxzk_bj_demo E/UserFragment: ------>onResume

沒啥的。

針對(duì)不設(shè)置setOffscreenPageLimit()跨琳,F(xiàn)ragmentPagerAdapter 結(jié)果我們分析一下:可以看到進(jìn)入到第一個(gè)Fragment的時(shí)候自点,也執(zhí)行了下一個(gè)Fragment的生命周期,執(zhí)行了不必要的操作脉让。那大家有沒有發(fā)現(xiàn)桂敛,如果那個(gè)Fragment的狀態(tài)為可見其setUserVisibleHint的值就為true,其余Fragment的值為false,那我們只需要判斷,如果setUserVisibleHint的值就為true即改Fragment為可見狀態(tài)溅潜,我們就執(zhí)行耗時(shí)操作术唬,其他Fragment為false,就不執(zhí)行網(wǎng)絡(luò)請(qǐng)求的操作唄。那我們寫一個(gè)公共的方法滚澜,注意此方法執(zhí)行粗仓,要放到onActivityCreate()之后,否則我請(qǐng)求回來的數(shù)據(jù)載體控件的Activity都沒有創(chuàng)建设捐,所以我要定義幾個(gè)變量來查看Fragment的狀態(tài),我們之前也說了Fragement有首次可見萝招,可見和不可見三種狀態(tài)蚂斤,代碼如下:


    View rootView;

    /**當(dāng)前Fragment是否首次可見,默認(rèn)是首次可見**/
    private boolean mIsFirstVisible = true;
    /**當(dāng)前Fragment的View是否已經(jīng)創(chuàng)建**/
    private boolean isViewCreated = false;
    /**當(dāng)前Fragment的可見狀態(tài)槐沼,一種當(dāng)前可見曙蒸,一種當(dāng)前不可見**/
    private boolean currentVisibleState = false;


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        LogUtil.e(getClass().getSimpleName(),"-----> onCreateView");

        if(rootView == null){
            rootView = inflater.inflate(getLayoutId(), container, false);
            initView(rootView);
        }
        isViewCreated=true;//在onCreateView執(zhí)行完畢,將isViewCreated改為true;
        return rootView;
    }

不同生命周期變量值的變更及涉及的相關(guān)代碼:

  • onStart
 @Override
    public void onStart() {
        super.onStart();
        LogUtil.e(getClass().getSimpleName(),"-----> onStart");
        //isHidden()是Fragment是否處于隱藏狀態(tài)和isVisible()有區(qū)別
        //getUserVisibleHint(),Fragement是否可見
        if(!isHidden()&& getUserVisibleHint()){//如果Fragment沒有隱藏且可見
         //執(zhí)行分發(fā)的方法,三種結(jié)果對(duì)應(yīng)自Fragment的三個(gè)回調(diào)岗钩,對(duì)應(yīng)的操作纽窟,F(xiàn)ragment首次加載,可見兼吓,不可見
            disPatchFragment(true);
        }

    }
  • onResume
  @Override
    public void onResume() {
        super.onResume();
        LogUtil.e(getClass().getSimpleName(),"-----> onResume");
        if(!mIsFirstVisible){
//表示點(diǎn)擊home鍵又返回操作,設(shè)置可見狀態(tài)為ture
            if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){
                disPatchFragment(true);
            }
        }

    }
  • onPause
   @Override
    public void onPause() {
        super.onPause();
        //表示點(diǎn)擊home鍵,原來可見的Fragment要走該方法臂港,更改Fragment的狀態(tài)為不可見
        if(!isHidden()&& getUserVisibleHint()){
            disPatchFragment(false);
        }
    }
  • onDestroyView
   @Override
    public void onDestroyView() {
        super.onDestroyView();
        LogUtil.e(getClass().getSimpleName(),"-----> onStart");
        //當(dāng) View 被銷毀的時(shí)候我們需要重新設(shè)置 isViewCreated mIsFirstVisible 的狀態(tài)
        isViewCreated = false;
        mIsFirstVisible = true;
    }

  • Fragment不同狀態(tài)對(duì)應(yīng)的回調(diào)方法
 /**
     *
     * @param visible Fragment當(dāng)前是否可見,然后調(diào)用相關(guān)方法
     */
    public  void   disPatchFragment(boolean visible){
        currentVisibleState=visible;
        if(visible){//Fragment可見
            if(mIsFirstVisible){//可見又是第一次
                mIsFirstVisible=false;//改變首次可見的狀態(tài)
                onFragmentFirst();
            }else{//可見但不是第一次
                LogUtil.e(getClass().getSimpleName(),"可見");
            }
        }else {//不可見
            LogUtil.e(getClass().getSimpleName(),"不可見");
        }
    };

    //Fragemnet首次可見的方法
    public void onFragmentFirst(){
        LogUtil.e(getClass().getSimpleName(),"首次可見");
    };
    //Fragemnet可見的方法
    public void onFragmentVisble(){//子Fragment調(diào)用次方法,執(zhí)行可見操作
        LogUtil.e(getClass().getSimpleName(),"可見");
    };
    //Fragemnet不可見的方法
    public void onFragmentInVisible(){
        LogUtil.e(getClass().getSimpleName(),"不可見");
    };

最后來一個(gè)總的代碼:
public abstract class BaseLazyLoadFragment extends android.support.v4.app.Fragment {

View rootView;

/**當(dāng)前Fragment是否首次可見趋艘,默認(rèn)是首次可見**/
private boolean mIsFirstVisible = true;
/**當(dāng)前Fragment的View是否已經(jīng)創(chuàng)建**/
private boolean isViewCreated = false;
/**當(dāng)前Fragment的可見狀態(tài)疲恢,一種當(dāng)前可見,一種當(dāng)前不可見**/
private boolean currentVisibleState = false;


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    LogUtil.e(getClass().getSimpleName(),"-----> onCreateView");

    if(rootView == null){
        rootView = inflater.inflate(getLayoutId(), container, false);
        initView(rootView);
    }
    isViewCreated=true;//在onCreateView執(zhí)行完畢瓷胧,將isViewCreated改為true;
    return rootView;
}


@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    LogUtil.e(getClass().getSimpleName(),"----->"+isVisibleToUser);
    if (isViewCreated) {
        if (isVisibleToUser && !currentVisibleState) {//Fragment可見且狀態(tài)不是可見(從一個(gè)Fragment切換到另外一個(gè)Fragment,后一個(gè)設(shè)置狀態(tài)為可見)
            disPatchFragment(true);
        } else if (!isVisibleToUser && currentVisibleState) {//Fragment不可見且狀態(tài)是可見(從一個(gè)Fragment切換到另外一個(gè)Fragment,前一個(gè)更改狀態(tài)為不可見)
            disPatchFragment(false);
        }
    }
}



/**返回子Fragment的布局id**/
public abstract  int getLayoutId();

/**初始化View的方法**/
public abstract  void initView(View rootView);




@Override
public void onStart() {
    super.onStart();
    LogUtil.e(getClass().getSimpleName(),"-----> onStart");
    //isHidden()是Fragment是否處于隱藏狀態(tài)和isVisible()有區(qū)別
    //getUserVisibleHint(),Fragement是否可見
    if(!isHidden()&& getUserVisibleHint()){//如果Fragment沒有隱藏且可見
     //執(zhí)行分發(fā)的方法,三種結(jié)果對(duì)應(yīng)自Fragment的三個(gè)回調(diào)显拳,對(duì)應(yīng)的操作,F(xiàn)ragment首次加載搓萧,可見杂数,不可見
        disPatchFragment(true);
    }

}


@Override
public void onResume() {
    super.onResume();
    LogUtil.e(getClass().getSimpleName(),"-----> onResume");
    if(!mIsFirstVisible){
        //表示點(diǎn)擊home鍵又返回操作,設(shè)置可見狀態(tài)為ture
        if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){
            disPatchFragment(true);
        }
    }

}


@Override
public void onPause() {
    super.onPause();
    //表示點(diǎn)擊home鍵,原來可見的Fragment要走該方法,更改Fragment的狀態(tài)為不可見
    if(!isHidden()&& getUserVisibleHint()){
        disPatchFragment(false);
    }
}


@Override
public void onDestroyView() {
    super.onDestroyView();
    LogUtil.e(getClass().getSimpleName(),"-----> onStart");
    //當(dāng) View 被銷毀的時(shí)候我們需要重新設(shè)置 isViewCreated mIsFirstVisible 的狀態(tài)
    isViewCreated = false;
    mIsFirstVisible = true;
}


/**
 *
 * @param visible Fragment當(dāng)前是否可見瘸洛,然后調(diào)用相關(guān)方法
 */
public  void   disPatchFragment(boolean visible){
    currentVisibleState=visible;
    if(visible){//Fragment可見
        if(mIsFirstVisible){//可見又是第一次
            mIsFirstVisible=false;//改變首次可見的狀態(tài)
            onFragmentFirst();
        }else{//可見但不是第一次
            LogUtil.e(getClass().getSimpleName(),"可見");
        }
    }else {//不可見
        LogUtil.e(getClass().getSimpleName(),"不可見");
    }
};

//Fragemnet首次可見的方法
public void onFragmentFirst(){
    LogUtil.e(getClass().getSimpleName(),"首次可見");
};
//Fragemnet可見的方法
public void onFragmentVisble(){//子Fragment調(diào)用次方法揍移,執(zhí)行可見操作
    LogUtil.e(getClass().getSimpleName(),"可見");
};
//Fragemnet不可見的方法
public void onFragmentInVisible(){
    LogUtil.e(getClass().getSimpleName(),"不可見");
};

}

我們的Fragment只需要繼承BaseLazyLoadFragment,然后對(duì)應(yīng)調(diào)用首次可見方法反肋,再次可見方法那伐,不可見方法做相應(yīng)的操作就可以了。

懶加載進(jìn)階

我們上面說的是一層的ViewPager加Fragment石蔗,但大家也一定遇到過Fragemgt中又來了一層ViewPager+Fragment罕邀,如圖:


那這種的怎么辦呢?之前的方法還管用不养距,別急诉探,我們先看看其生命周期打印:

納尼棍厌,InvestFragment(投資)內(nèi)部第一個(gè)Tab,Invest_MineFragment竟然執(zhí)行了首次可見的方法肾胯,要知道此時(shí)的InvestFragmetn都沒有顯示。
針對(duì)此問題耘纱,我的解決方法是敬肚,先判斷父Fragment如果沒有顯示,那么不觸發(fā)我們定義的方法揣炕,代碼如下:

  /**
     *判斷多層嵌套的父Fragment是否顯示
     */
    private boolean isParentFragmentInvisible() {
        //獲取父Fragment的可見狀態(tài)
        BaseLazyLoadFragment fragment = (BaseLazyLoadFragment) getParentFragment();
   //如果兩個(gè)都滿足即父Fragment不為空且父Fragment不可見帘皿,返回true
        return fragment != null && !fragment.getCurrentVisibleState();
    }
    
    private boolean getCurrentVisibleState() {
        return currentVisibleState;
    }

ok,我們?cè)谶\(yùn)行一下,打印如下:

image.png

感覺沒啥問題?
別急畸陡,還有剩余部分打印信息:
image.png

也就是我們還需要一個(gè)第一個(gè)子Fragment的狀態(tài)信息:解決思路如下:
由于父Fragment的執(zhí)行在子Fragment之前,所以虽填,當(dāng)我們?cè)诟?Fragment 分發(fā)完成自己的可見事件后丁恭,讓子 Fragment 再次調(diào)用自己的可見事件分發(fā)方法,這次我們讓 isParentFragmentVsible() 返回 false 斋日,可見狀態(tài)將會(huì)正確分發(fā)了牲览,有點(diǎn)類似于父類完成后,又調(diào)用方法刷新子類
恶守。

 /**
     *
     * @param visible Fragment當(dāng)前是否可見第献,然后調(diào)用相關(guān)方法
     */
    public  void   disPatchFragment(boolean visible){
        String aa =getClass().getSimpleName();
        //如果父Fragment不可見,則不向下分發(fā)給子Fragment
        if(visible && isParentFragmentVsible())return;
        currentVisibleState=visible;
        if(visible){//Fragment可見
            if(mIsFirstVisible){//可見又是第一次
                mIsFirstVisible=false;//改變首次可見的狀態(tài)
                onFragmentFirst();
            }//可見但不是第一次
                onFragmentVisble();
            //可見狀態(tài)的時(shí)候內(nèi)層 fragment 生命周期晚于外層 所以在 onFragmentResume 后分發(fā)
            dispatchChildFragmentVisibleState(true);
        }else {//不可見
            onFragmentInVisible();
            dispatchChildFragmentVisibleState(false);
        }
    };


    /**
     * 父Fragment分發(fā)完成之后再次調(diào)用贡必,重新分發(fā)給子Fragment
     * @param visible
     */
    private void dispatchChildFragmentVisibleState(boolean visible) {
        FragmentManager childFragmentManager = getChildFragmentManager();
        @SuppressLint("RestrictedApi") List<Fragment> fragments = childFragmentManager.getFragments();
        if(fragments != null){
            if (!fragments.isEmpty()) {
                for (Fragment child : fragments) {
                    if (child instanceof BaseLazyLoadFragment && !child.isHidden() && child.getUserVisibleHint()) {
                        ((BaseLazyLoadFragment) child).disPatchFragment(visible);
                    }
                }
            }
        }

    }

這樣就ok了,然后我們點(diǎn)擊home鍵:


這又是什么問題庸毫,Invest_YourFragment為什么走了兩邊仔拟?
原因處在順序調(diào)用上,我剛才說了:父 Fragment總是優(yōu)先于子 Fragment飒赃,而對(duì)于不可見事件利花,內(nèi)部的 Fragment 生命周期總是先于外層 Fragment≡丶眩回到我們代碼里:父Fragment調(diào)用自身的 disPatchFragment方法分發(fā)了不可見事件,又會(huì)再次調(diào)用 dispatchChildFragmentVisibleState 炒事,導(dǎo)致子 Fragment 再次調(diào)用自己的 disPatchFragment再次調(diào)用了一次 不可見事件onFragmentInVisible,故產(chǎn)生了兩次蔫慧。

解決辦法就是我們之前定義的變量:currentVisibleState挠乳,如果當(dāng)前的 Fragment 要分發(fā)的狀態(tài)與 currentVisibleState 相同我們就沒有必要去做分發(fā)了。
代碼及添加位置如下:
``


image.png

``
最后附上總代碼姑躲,編寫Fragment時(shí)欲侮,只需要繼承該類,然后調(diào)用可見的方法就好了肋联。

public abstract class BaseLazyLoadFragment extends Fragment {
    
    View rootView;

    /**當(dāng)前Fragment是否首次可見威蕉,默認(rèn)是首次可見**/
    private boolean mIsFirstVisible = true;
    /**當(dāng)前Fragment的View是否已經(jīng)創(chuàng)建**/
    private boolean isViewCreated = false;
    /**當(dāng)前Fragment的可見狀態(tài),一種當(dāng)前可見橄仍,一種當(dāng)前不可見**/
    private boolean currentVisibleState = false;


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        LogUtil.e(getClass().getSimpleName(),"-----> onCreateView");

        if(rootView == null){
            rootView = inflater.inflate(getLayoutId(), container, false);
            initView(rootView);
        }
        isViewCreated=true;//在onCreateView執(zhí)行完畢韧涨,將isViewCreated改為true;
        return rootView;
    }



    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isViewCreated) {
            if (isVisibleToUser && !currentVisibleState) {//Fragment可見且狀態(tài)不是可見(從一個(gè)Fragment切換到另外一個(gè)Fragment,后一個(gè)設(shè)置狀態(tài)為可見)
                disPatchFragment(true);
            } else if (!isVisibleToUser && currentVisibleState) {//Fragment不可見且狀態(tài)是可見(從一個(gè)Fragment切換到另外一個(gè)Fragment,前一個(gè)更改狀態(tài)為不可見)
                disPatchFragment(false);
            }
        }
    }



    /**返回子Fragment的布局id**/
    public abstract  int getLayoutId();

    /**初始化View的方法**/
    public abstract  void initView(View rootView);




    @Override
    public void onStart() {
        super.onStart();

        //isHidden()是Fragment是否處于隱藏狀態(tài)和isVisible()有區(qū)別
        //getUserVisibleHint(),Fragement是否可見
        if(!isHidden()&& getUserVisibleHint()){//如果Fragment沒有隱藏且可見
         //執(zhí)行分發(fā)的方法,三種結(jié)果對(duì)應(yīng)自Fragment的三個(gè)回調(diào),對(duì)應(yīng)的操作侮繁,F(xiàn)ragment首次加載虑粥,可見,不可見
            disPatchFragment(true);
        }

    }


    @Override
    public void onResume() {
        super.onResume();
        if(!mIsFirstVisible){
            //表示點(diǎn)擊home鍵又返回操作,設(shè)置可見狀態(tài)為ture
            if(!isHidden()&& !getUserVisibleHint() && currentVisibleState){
                disPatchFragment(true);
            }
        }

    }


    @Override
    public void onPause() {
        super.onPause();
        //表示點(diǎn)擊home鍵,原來可見的Fragment要走該方法宪哩,更改Fragment的狀態(tài)為不可見
        if(!isHidden()&& getUserVisibleHint()){
            disPatchFragment(false);
        }
    }


    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //當(dāng) View 被銷毀的時(shí)候我們需要重新設(shè)置 isViewCreated mIsFirstVisible 的狀態(tài)
        isViewCreated = false;
        mIsFirstVisible = true;
    }


    /**
     *
     * @param visible Fragment當(dāng)前是否可見娩贷,然后調(diào)用相關(guān)方法
     */
    public  void   disPatchFragment(boolean visible){
        String aa =getClass().getSimpleName();
        //如果父Fragment不可見,則不向下分發(fā)給子Fragment
        if(visible && isParentFragmentVsible())return;

        // 如果當(dāng)前的 Fragment 要分發(fā)的狀態(tài)與 currentVisibleState 相同(都為false)我們就沒有必要去做分發(fā)了。
        if (currentVisibleState == visible) {
            return;
        }

        currentVisibleState=visible;
        if(visible){//Fragment可見
            if(mIsFirstVisible){//可見又是第一次
                mIsFirstVisible=false;//改變首次可見的狀態(tài)
                onFragmentFirst();
            }//可見但不是第一次
                onFragmentVisble();
            //可見狀態(tài)的時(shí)候內(nèi)層 fragment 生命周期晚于外層 所以在 onFragmentResume 后分發(fā)
            dispatchChildFragmentVisibleState(true);
        }else {//不可見
            onFragmentInVisible();
            dispatchChildFragmentVisibleState(false);
        }
    };


    /**
     * 重新分發(fā)給子Fragment
     * @param visible
     */
    private void dispatchChildFragmentVisibleState(boolean visible) {
        FragmentManager childFragmentManager = getChildFragmentManager();
        @SuppressLint("RestrictedApi") List<Fragment> fragments = childFragmentManager.getFragments();
        if(fragments != null){
            if (!fragments.isEmpty()) {
                for (Fragment child : fragments) {
                    if (child instanceof BaseLazyLoadFragment && !child.isHidden() && child.getUserVisibleHint()) {
                        ((BaseLazyLoadFragment) child).disPatchFragment(visible);
                    }
                }
            }
        }

    }

    //Fragemnet首次可見的方法
    public void onFragmentFirst(){
        LogUtil.e(getClass().getSimpleName(),"首次可見");
    };
    //Fragemnet可見的方法
    public void onFragmentVisble(){//子Fragment調(diào)用次方法锁孟,執(zhí)行可見操作
        LogUtil.e(getClass().getSimpleName(),"可見");
    };
    //Fragemnet不可見的方法
    public void onFragmentInVisible(){
        LogUtil.e(getClass().getSimpleName(),"不可見");
    };
    

    /**
     *判斷多層嵌套的父Fragment是否顯示
     */
    private boolean isParentFragmentVsible() {
        BaseLazyLoadFragment fragment = (BaseLazyLoadFragment) getParentFragment();
        return fragment != null && !fragment.getCurrentVisibleState();
    }

    private boolean getCurrentVisibleState() {
        return currentVisibleState;
    }
}

完畢彬祖!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市品抽,隨后出現(xiàn)的幾起案子储笑,更是在濱河造成了極大的恐慌,老刑警劉巖圆恤,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件突倍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)羽历,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門焊虏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秕磷,你說我怎么就攤上這事诵闭。” “怎么了跳夭?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵涂圆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我币叹,道長(zhǎng)润歉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任颈抚,我火速辦了婚禮踩衩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贩汉。我一直安慰自己驱富,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布匹舞。 她就那樣靜靜地躺著褐鸥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赐稽。 梳的紋絲不亂的頭發(fā)上叫榕,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音姊舵,去河邊找鬼晰绎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛括丁,可吹牛的內(nèi)容都是我干的荞下。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼史飞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼尖昏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起祸憋,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤会宪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蚯窥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年拦赠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巍沙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荷鼠,死狀恐怖句携,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情允乐,我是刑警寧澤矮嫉,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站牍疏,受9級(jí)特大地震影響蠢笋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鳞陨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一昨寞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厦滤,春花似錦援岩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至趟咆,卻和暖如春添瓷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背忍啸。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工仰坦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人计雌。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓悄晃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親凿滤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妈橄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 目錄介紹 1.什么是懶加載1.1 什么是預(yù)加載1.2 懶加載介紹1.3 懶加載概括 2.實(shí)際應(yīng)用中有哪些懶加載案例...
    楊充211閱讀 9,657評(píng)論 4 15
  • 多層嵌套后的 Fragment 懶加載 印象中從 Feed 流應(yīng)用流行開始,F(xiàn)ragment 懶加載變成了一個(gè)大家...
    醒著的碼者閱讀 3,812評(píng)論 3 60
  • ???最近在重構(gòu)公司公司的主頁(yè)翁脆,其中用到了懶加載眷蚓,所以找了網(wǎng)上的懶加載的介紹的博客,讀了好幾篇關(guān)于懶加載的文件反番,發(fā)...
    Jinwong閱讀 8,928評(píng)論 1 37
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,777評(píng)論 25 707
  • 飯后沙热,躺在床上叉钥,無(wú)聊的翻看著手機(jī),其實(shí)也沒啥好看的篙贸,只是覺得躺著如果不拿個(gè)手機(jī)投队,就失去了躺下的意義。不知道從什么時(shí)...
    楚心羽閱讀 574評(píng)論 0 3