您真的懂fragment的onResume,setUserVisibleHint,onHiddenChanged,isVisible方法嗎!

say_from_wen發表於2018-05-19

寫在開頭

最近公司的一個專案需要的Fragment可見的時候處理一些邏輯,UI結構並非Tablayout+viewPager+Fragment結果,而是FragmentTabHost+Fragment的結構,所以有了一些坑,不知道你是否遇到過,從原始碼層面看一下這些問題,寫出來希望大家判斷好與壞。

思考:

公司之前程式碼是在onResume方法中寫邏輯,後來想了下,這明顯是不對的,大家都知道Fragment的onResume是依賴於附屬Activity的onResume方法的,當你從fragment的跳轉到另一個Activity再次返回的時候,fragment附屬的Activity下的所有Fragment都會走onResume方法,我們專案中onResume方法都是一些必須的網路請求和一些與邏輯無關的操作,所以並未發現錯誤,在我動態適配狀態的過程發現了這個問題並研究了一下,下面來看這些方法!

採坑一

setUserVisibleHint方法,先看一下原始碼:

public void setUserVisibleHint(boolean isVisibleToUser) {
        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
                && mFragmentManager != null && isAdded()) {
            mFragmentManager.performPendingDeferredStart(this);
        }
        mUserVisibleHint = isVisibleToUser;
        mDeferStart = mState < STARTED && !isVisibleToUser;
    }
複製程式碼

我們可以看到,他只是Fragment原始碼中的一個方法,這說明他是需要手動呼叫的,那為什麼Fragment+ViewPager架構的可以用這個方法來判斷Fragment是否可見呢?那麼就需要看一下Viewpager和與之結合的FragmentPagerAdapter原始碼了,如下:

@Override
public Object instantiateItem(ViewGroup container, int position) {
	    ...
	    
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }
        return fragment;
    }
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment)object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
 
mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

複製程式碼

這個方法呼叫實際在FragmentPagerAdapter中。那麼當你是FragmentTabHost+Fragment的結構的時候,你會發現這個方法壓根不會被呼叫。

採坑二

onHiddenChanged方法。原始碼的註釋寫的很清楚了。

/**
     * Return true if the fragment has been hidden.  By default fragments
     * are shown.  You can find out about changes to this state with
     * {@link #onHiddenChanged}.  Note that the hidden state is orthogonal
     * to other states -- that is, to be visible to the user, a fragment
     * must be both started and not hidden.
     */
	
如果該Fragment物件已經被隱藏,那麼它返回true。預設情況下,Fragment是被顯示的。能夠用onHiddenChanged(boolean)回撥方法獲取該Fragment物件狀態的改變,要注意的是隱藏狀態與其他狀態是正交的---也就是說,要把該Fragment物件顯示給使用者,Fragment物件必須是被啟動並不被隱藏。

#### 值得我們注意的是
這裡的隱藏或者顯示是指Fragment呼叫show或者hider的時候才會改變mHidden的值得

複製程式碼

在FragmentTabHost+Fragment的結構的時候,當你跳轉到另一個Activity再次返回的時候你會發現這方法並沒有走,因為當前Fragment並沒有改變show或者hide,故不會走。

採坑三:

isVisible方法

如果使用這個方法判斷當前頁面是否隱藏了呢?我試了也是不行的,先看下這個方法的原始碼:

	 final public boolean isVisible() {
        return isAdded() && !isHidden() && mView != null
                && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
    }
複製程式碼

如果你懂了onHiddenChanged方法,這應該就知道只使用它也是不行的,因為isHidden()的值和onHiddenChanged方法是有聯絡的。當你切換tab的時候,isVisible()放回是false。

結論

趁這次需求,也詳細看了下這些生命週期的詳細理論,所以在FragmentTabHost+Fragment的結構的時候我是這樣解決的,如下:能適配第一次建立,tab切換,跳轉Activity再次返回多種情況。

	    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            onResumeCommon();
        }
    }

    //isVisible()  重點是原始碼中isHidden的值
    //如果該Fragment物件已經被隱藏,也就是執行fragment執行hide()物件後,那麼它返回true。
    @Override
    public void onResume() {
        super.onResume();
        if (isVisible()) {
            onResumeCommon();
        }
    }

    private void onResumeCommon() {
            StatusBarUtil.setStatusBarColor(mActivity, R.color.white);
            StatusBarUtil.StatusBarLightMode(mActivity);
    }
複製程式碼

寫在最後

知識沒有學完了的一天,繼續努力!!!

相關文章