[Android]Fragment源碼分析(二) 狀態,androidfragment
我們上一講,拋出來一個問題,就是當Activity的onCreateView的時候,是如何構造Fragment中的View參數。要回答這個問題我們先要瞭解Fragment的狀態,這是Fragment管理中非常重要的一環。我們先來看一下FragmentActivity提供的一些核心回調:
@Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer, null); // Old versions of the platform didn't do this! if (getLayoutInflater().getFactory() == null) { getLayoutInflater().setFactory(this); } super.onCreate(savedInstanceState);.... mFragments.dispatchCreate(); }我們跟入mFragments.dispatchCreate方法中:
public void dispatchCreate() { mStateSaved = false; moveToState(Fragment.CREATED, false); }
我們看到,對於FragmentManager來說,做了一次狀態轉換。我上一篇說過FragmentManager是及其重要的類,它承擔了Fragment管理最為核心的工作。它有它自身的狀態機器,而它的狀態,可以理解為與Activity本身基本同步。
在Fm裡面維護自己的一個狀態,當你匯入一個Fragment的時候,Fm的目的,就是為了讓Fragment和自己的狀態基本保持一致.
void moveToState(int newState, int transit, int transitStyle, boolean always) { if (mActivity == null && newState != Fragment.INITIALIZING) { throw new IllegalStateException("No activity"); } if (!always && mCurState == newState) { return; } mCurState = newState; if (mActive != null) { boolean loadersRunning = false; for (int i = 0; i < mActive.size(); i++) { Fragment f = mActive.get(i); if (f != null) { moveToState(f, newState, transit, transitStyle, false); if (f.mLoaderManager != null) { loadersRunning |= f.mLoaderManager.hasRunningLoaders(); } } } ... } }
我們看到,FragmentManager的每一次狀態變更,都會引起mActive裡面的Fragment的狀態變更。而mActive是所有納入FragmentManager管理的Fragment容器。我們來看一下Fragment的幾個狀態:
static final int INITIALIZING = 0; // Not yet created. static final int CREATED = 1; // Created. static final int ACTIVITY_CREATED = 2; // The activity has finished its creation. static final int STOPPED = 3; // Fully created, not started. static final int STARTED = 4; // Created and started, not resumed. static final int RESUMED = 5; // Created started and resumed.
可以看出實際上,你的狀態越靠後你的狀態值越大,實際上在Fm的管理中,也巧妙的用到了這一點。
if (f.mState < newState) { ...} else { ...}
對於f.mState<newState可以理解為創造的過程。同時我們也能找到我們上一篇文章的問題的答案:
if (f.mFromLayout) { // For fragments that are part of the content view // layout, we need to instantiate the view immediately // and the inflater will take care of adding it. f.mView = f.performCreateView( f.getLayoutInflater(f.mSavedFragmentState), null, f.mSavedFragmentState); if (f.mView != null) { f.mInnerView = f.mView; f.mView = NoSaveStateFrameLayout.wrap(f.mView); if (f.mHidden) f.mView.setVisibility(View.GONE); f.onViewCreated(f.mView, f.mSavedFragmentState); } else { f.mInnerView = null; } }
f.mFromLayout代表的是你這個Fragment的產生是否是從layout.xml檔案中產生的。而它的View的產生是調用performCreateView來產生的。
View performCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mChildFragmentManager != null) { mChildFragmentManager.noteStateNotSaved(); } return onCreateView(inflater, container, savedInstanceState); }
對,這裡就是我們非常熟悉的onCreateView回調的出處。當然我們現在還是屬於Fragment.INITIALIZING這個狀態。但實際上,我們在調用Fragment的時候FragmentManageer已經進入了Create狀態。也就是說newState參數應該是Create才對。所以我們接著代碼往下走:
case Fragment.CREATED: if (newState > Fragment.CREATED) { if (!f.mFromLayout) { ViewGroup container = null; if (f.mContainerId != 0) { container = (ViewGroup) mContainer .findViewById(f.mContainerId); if (container == null && !f.mRestored) { throwException(new IllegalArgumentException( "No view found for id 0x" + Integer .toHexString(f.mContainerId) + " (" + f.getResources() .getResourceName( f.mContainerId) + ") for fragment " + f)); } } f.mContainer = container; f.mView = f.performCreateView( f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState); if (f.mView != null) { f.mInnerView = f.mView; f.mView = NoSaveStateFrameLayout.wrap(f.mView); if (container != null) { Animation anim = loadAnimation(f, transit, true, transitionStyle); if (anim != null) { f.mView.startAnimation(anim); } container.addView(f.mView); } if (f.mHidden) f.mView.setVisibility(View.GONE); f.onViewCreated(f.mView, f.mSavedFragmentState); } else { f.mInnerView = null; } } f.performActivityCreated(f.mSavedFragmentState); if (f.mView != null) { f.restoreViewState(f.mSavedFragmentState); } f.mSavedFragmentState = null; }我們看到實際上這段代碼是對FragmentManager狀態是Create以上狀態且Fragment的匯入並不是採用layout.xml方式匯入的處理。這是為什麼呢?因為在onCreate之後,基本上你的控制項已經在Create狀態的時候產生的差不多了,你所要做的就是在產生的控制項中找到Fragment對應的容器,然後裝入你的控制項。同時,我們也看到了對Fragment的動畫處理:
if (f.mView != null) { f.mInnerView = f.mView; f.mView = NoSaveStateFrameLayout.wrap(f.mView); if (container != null) { Animation anim = loadAnimation(f, transit, true, transitionStyle); if (anim != null) { f.mView.startAnimation(anim); } container.addView(f.mView); } if (f.mHidden) f.mView.setVisibility(View.GONE); f.onViewCreated(f.mView, f.mSavedFragmentState); } else { f.mInnerView = null; }而這種動畫的處理和參數的配置,我們留到後面講到Fragment事務的時候再說。
Android fragment使用中遇到的問題?
把gridview的fragment的布局和gridview的item的布局貼出來看看。
android的Fragment切換時怎儲存狀態,避免重複調用onCreateView()
在sdk sample的APIdemo 的FragmentHideShow.java