Fix bugs around inflated child fragments
Framework edition * Ensure that a fragment that has had its view destroyed has a chance to recreate it. * Clear mInLayout after views are destroyed * Make sure that when a fragment is restored from the back stack and reinflates its view that child fragments from fragment tags are reconnected properly. Bug: 32691935 Test: cts Change-Id: I370d6db46e72fd8ac35f716e2130b9e10b93b1da
This commit is contained in:
@@ -414,6 +414,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
|
||||
// True if this fragment has been restored from previously saved state.
|
||||
boolean mRestored;
|
||||
|
||||
// True if performCreateView has been called and a matching call to performDestroyView
|
||||
// has not yet happened.
|
||||
boolean mPerformedCreateView;
|
||||
|
||||
// Number of active back stack entries this fragment is in.
|
||||
int mBackStackNesting;
|
||||
|
||||
@@ -611,7 +615,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
|
||||
Fragment f = (Fragment)clazz.newInstance();
|
||||
if (args != null) {
|
||||
args.setClassLoader(f.getClass().getClassLoader());
|
||||
f.mArguments = args;
|
||||
f.setArguments(args);
|
||||
}
|
||||
return f;
|
||||
} catch (ClassNotFoundException e) {
|
||||
@@ -2464,6 +2468,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
|
||||
if (mChildFragmentManager != null) {
|
||||
mChildFragmentManager.noteStateNotSaved();
|
||||
}
|
||||
mPerformedCreateView = true;
|
||||
return onCreateView(inflater, container, savedInstanceState);
|
||||
}
|
||||
|
||||
@@ -2690,6 +2695,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
|
||||
if (mLoaderManager != null) {
|
||||
mLoaderManager.doReportNextStart();
|
||||
}
|
||||
mPerformedCreateView = false;
|
||||
}
|
||||
|
||||
void performDestroy() {
|
||||
|
||||
@@ -1072,7 +1072,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
|
||||
if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
|
||||
newState = Fragment.STOPPED;
|
||||
}
|
||||
if (f.mState < newState) {
|
||||
if (f.mState <= newState) {
|
||||
// For fragments that are created from a layout, when restoring from
|
||||
// state we don't want to allow them to be created until they are
|
||||
// being reloaded from the layout.
|
||||
@@ -1089,65 +1089,59 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
|
||||
}
|
||||
switch (f.mState) {
|
||||
case Fragment.INITIALIZING:
|
||||
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
|
||||
if (f.mSavedFragmentState != null) {
|
||||
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
|
||||
FragmentManagerImpl.VIEW_STATE_TAG);
|
||||
f.mTarget = getFragment(f.mSavedFragmentState,
|
||||
FragmentManagerImpl.TARGET_STATE_TAG);
|
||||
if (f.mTarget != null) {
|
||||
f.mTargetRequestCode = f.mSavedFragmentState.getInt(
|
||||
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
|
||||
}
|
||||
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
|
||||
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
|
||||
if (!f.mUserVisibleHint) {
|
||||
f.mDeferStart = true;
|
||||
if (newState > Fragment.STOPPED) {
|
||||
newState = Fragment.STOPPED;
|
||||
if (newState > Fragment.INITIALIZING) {
|
||||
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
|
||||
if (f.mSavedFragmentState != null) {
|
||||
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
|
||||
FragmentManagerImpl.VIEW_STATE_TAG);
|
||||
f.mTarget = getFragment(f.mSavedFragmentState,
|
||||
FragmentManagerImpl.TARGET_STATE_TAG);
|
||||
if (f.mTarget != null) {
|
||||
f.mTargetRequestCode = f.mSavedFragmentState.getInt(
|
||||
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
|
||||
}
|
||||
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
|
||||
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
|
||||
if (!f.mUserVisibleHint) {
|
||||
f.mDeferStart = true;
|
||||
if (newState > Fragment.STOPPED) {
|
||||
newState = Fragment.STOPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f.mHost = mHost;
|
||||
f.mParentFragment = mParent;
|
||||
f.mFragmentManager = mParent != null
|
||||
? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
|
||||
dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
|
||||
f.mCalled = false;
|
||||
f.onAttach(mHost.getContext());
|
||||
if (!f.mCalled) {
|
||||
throw new SuperNotCalledException("Fragment " + f
|
||||
+ " did not call through to super.onAttach()");
|
||||
}
|
||||
if (f.mParentFragment == null) {
|
||||
mHost.onAttachFragment(f);
|
||||
} else {
|
||||
f.mParentFragment.onAttachFragment(f);
|
||||
}
|
||||
dispatchOnFragmentAttached(f, mHost.getContext(), false);
|
||||
|
||||
if (!f.mRetaining) {
|
||||
f.performCreate(f.mSavedFragmentState);
|
||||
dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
|
||||
} else {
|
||||
f.restoreChildFragmentState(f.mSavedFragmentState, true);
|
||||
f.mState = Fragment.CREATED;
|
||||
}
|
||||
f.mRetaining = false;
|
||||
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.mView.setSaveFromParentEnabled(false);
|
||||
if (f.mHidden) f.mView.setVisibility(View.GONE);
|
||||
f.onViewCreated(f.mView, f.mSavedFragmentState);
|
||||
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
|
||||
f.mHost = mHost;
|
||||
f.mParentFragment = mParent;
|
||||
f.mFragmentManager = mParent != null
|
||||
? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
|
||||
dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
|
||||
f.mCalled = false;
|
||||
f.onAttach(mHost.getContext());
|
||||
if (!f.mCalled) {
|
||||
throw new SuperNotCalledException("Fragment " + f
|
||||
+ " did not call through to super.onAttach()");
|
||||
}
|
||||
if (f.mParentFragment == null) {
|
||||
mHost.onAttachFragment(f);
|
||||
} else {
|
||||
f.mParentFragment.onAttachFragment(f);
|
||||
}
|
||||
dispatchOnFragmentAttached(f, mHost.getContext(), false);
|
||||
|
||||
if (!f.mRetaining) {
|
||||
f.performCreate(f.mSavedFragmentState);
|
||||
dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
|
||||
} else {
|
||||
f.restoreChildFragmentState(f.mSavedFragmentState, true);
|
||||
f.mState = Fragment.CREATED;
|
||||
}
|
||||
f.mRetaining = false;
|
||||
}
|
||||
case Fragment.CREATED:
|
||||
// This is outside the if statement below on purpose; we want this to run
|
||||
// even if we do a moveToState from CREATED => *, CREATED => CREATED, and
|
||||
// * => CREATED as part of the case fallthrough above.
|
||||
ensureInflatedFragmentView(f);
|
||||
|
||||
if (newState > Fragment.CREATED) {
|
||||
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
|
||||
if (!f.mFromLayout) {
|
||||
@@ -1281,6 +1275,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
|
||||
}
|
||||
f.mContainer = null;
|
||||
f.mView = null;
|
||||
f.mInLayout = false;
|
||||
}
|
||||
case Fragment.CREATED:
|
||||
if (newState < Fragment.CREATED) {
|
||||
@@ -1340,6 +1335,19 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
|
||||
moveToState(f, mCurState, 0, 0, false);
|
||||
}
|
||||
|
||||
void ensureInflatedFragmentView(Fragment f) {
|
||||
if (f.mFromLayout && !f.mPerformedCreateView) {
|
||||
f.mView = f.performCreateView(f.getLayoutInflater(
|
||||
f.mSavedFragmentState), null, f.mSavedFragmentState);
|
||||
if (f.mView != null) {
|
||||
f.mView.setSaveFromParentEnabled(false);
|
||||
if (f.mHidden) f.mView.setVisibility(View.GONE);
|
||||
f.onViewCreated(f.mView, f.mSavedFragmentState);
|
||||
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragments that have been shown or hidden don't have their visibility changed or
|
||||
* animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
|
||||
@@ -3262,7 +3270,9 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
|
||||
}
|
||||
|
||||
// If we haven't finished entering the CREATED state ourselves yet,
|
||||
// push the inflated child fragment along.
|
||||
// push the inflated child fragment along. This will ensureInflatedFragmentView
|
||||
// at the right phase of the lifecycle so that we will have mView populated
|
||||
// for compliant fragments below.
|
||||
if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
|
||||
moveToState(fragment, Fragment.CREATED, 0, 0, false);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user