diff options
| -rw-r--r-- | api/current.xml | 41 | ||||
| -rw-r--r-- | core/java/android/app/BackStackRecord.java | 48 | ||||
| -rw-r--r-- | core/java/android/app/Fragment.java | 37 | ||||
| -rw-r--r-- | core/java/android/app/FragmentManager.java | 97 | ||||
| -rw-r--r-- | core/java/android/app/FragmentTransaction.java | 25 | ||||
| -rw-r--r-- | core/java/android/app/ListFragment.java | 6 |
6 files changed, 219 insertions, 35 deletions
diff --git a/api/current.xml b/api/current.xml index b2330beb3409..3843f676bfc8 100644 --- a/api/current.xml +++ b/api/current.xml @@ -30529,6 +30529,21 @@ visibility="public" > </method> +<method name="onViewCreated" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="view" type="android.view.View"> +</parameter> +<parameter name="savedInstanceState" type="android.os.Bundle"> +</parameter> +</method> <method name="registerForContextMenu" return="void" abstract="false" @@ -31262,6 +31277,19 @@ <parameter name="name" type="java.lang.String"> </parameter> </method> +<method name="attach" + return="android.app.FragmentTransaction" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fragment" type="android.app.Fragment"> +</parameter> +</method> <method name="commit" return="int" abstract="true" @@ -31284,6 +31312,19 @@ visibility="public" > </method> +<method name="detach" + return="android.app.FragmentTransaction" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="fragment" type="android.app.Fragment"> +</parameter> +</method> <method name="disallowAddToBackStack" return="android.app.FragmentTransaction" abstract="true" diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 1d217f030221..09e3d76e82c6 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -169,6 +169,8 @@ final class BackStackRecord extends FragmentTransaction implements static final int OP_REMOVE = 3; static final int OP_HIDE = 4; static final int OP_SHOW = 5; + static final int OP_DETACH = 6; + static final int OP_ATTACH = 7; static final class Op { Op next; @@ -401,6 +403,32 @@ final class BackStackRecord extends FragmentTransaction implements return this; } + public FragmentTransaction detach(Fragment fragment) { + //if (fragment.mImmediateActivity == null) { + // throw new IllegalStateException("Fragment not added: " + fragment); + //} + + Op op = new Op(); + op.cmd = OP_DETACH; + op.fragment = fragment; + addOp(op); + + return this; + } + + public FragmentTransaction attach(Fragment fragment) { + //if (fragment.mImmediateActivity == null) { + // throw new IllegalStateException("Fragment not added: " + fragment); + //} + + Op op = new Op(); + op.cmd = OP_ATTACH; + op.fragment = fragment; + addOp(op); + + return this; + } + public FragmentTransaction setCustomAnimations(int enter, int exit) { mEnterAnim = enter; mExitAnim = exit; @@ -567,6 +595,16 @@ final class BackStackRecord extends FragmentTransaction implements f.mNextAnim = op.enterAnim; mManager.showFragment(f, mTransition, mTransitionStyle); } break; + case OP_DETACH: { + Fragment f = op.fragment; + f.mNextAnim = op.exitAnim; + mManager.detachFragment(f, mTransition, mTransitionStyle); + } break; + case OP_ATTACH: { + Fragment f = op.fragment; + f.mNextAnim = op.enterAnim; + mManager.attachFragment(f, mTransition, mTransitionStyle); + } break; default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } @@ -627,6 +665,16 @@ final class BackStackRecord extends FragmentTransaction implements mManager.hideFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); } break; + case OP_DETACH: { + Fragment f = op.fragment; + mManager.attachFragment(f, + FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); + } break; + case OP_ATTACH: { + Fragment f = op.fragment; + mManager.detachFragment(f, + FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); + } break; default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 53dc7c8a0c4a..dd158f94fbab 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -52,6 +52,7 @@ final class FragmentState implements Parcelable { final int mContainerId; final String mTag; final boolean mRetainInstance; + final boolean mDetached; final Bundle mArguments; Bundle mSavedFragmentState; @@ -66,6 +67,7 @@ final class FragmentState implements Parcelable { mContainerId = frag.mContainerId; mTag = frag.mTag; mRetainInstance = frag.mRetainInstance; + mDetached = frag.mDetached; mArguments = frag.mArguments; } @@ -77,6 +79,7 @@ final class FragmentState implements Parcelable { mContainerId = in.readInt(); mTag = in.readString(); mRetainInstance = in.readInt() != 0; + mDetached = in.readInt() != 0; mArguments = in.readBundle(); mSavedFragmentState = in.readBundle(); } @@ -103,6 +106,7 @@ final class FragmentState implements Parcelable { mInstance.mContainerId = mContainerId; mInstance.mTag = mTag; mInstance.mRetainInstance = mRetainInstance; + mInstance.mDetached = mDetached; mInstance.mFragmentManager = activity.mFragments; return mInstance; @@ -120,6 +124,7 @@ final class FragmentState implements Parcelable { dest.writeInt(mContainerId); dest.writeString(mTag); dest.writeInt(mRetainInstance ? 1 : 0); + dest.writeInt(mDetached ? 1 : 0); dest.writeBundle(mArguments); dest.writeBundle(mSavedFragmentState); } @@ -321,8 +326,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener 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 STARTED = 3; // Created and started, not resumed. - static final int RESUMED = 4; // Created started and resumed. + 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. int mState = INITIALIZING; @@ -404,6 +410,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener // from the user. boolean mHidden; + // Set to true when the app has requested that this fragment be detached. + boolean mDetached; + // If set this fragment would like its instance retained across // configuration changes. boolean mRetainInstance; @@ -511,23 +520,27 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener } } - void restoreViewState() { + final void restoreViewState() { if (mSavedViewState != null) { mView.restoreHierarchyState(mSavedViewState); mSavedViewState = null; } } - void setIndex(int index) { + final void setIndex(int index) { mIndex = index; mWho = "android:fragment:" + mIndex; } - void clearIndex() { + final void clearIndex() { mIndex = -1; mWho = null; } + final boolean isInBackStack() { + return mBackStackNesting > 0; + } + /** * Subclasses can not override equals(). */ @@ -947,6 +960,19 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener } /** + * Called immediately after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} + * has returned, but before any saved state has been restored in to the view. + * This gives subclasses a chance to initialize themselves once + * they know their view hierarchy has been completely created. The fragment's + * view hierarchy is not however attached to its parent at this point. + * @param view The View returned by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. + * @param savedInstanceState If non-null, this fragment is being re-constructed + * from a previous saved state as given here. + */ + public void onViewCreated(View view, Bundle savedInstanceState) { + } + + /** * Called to have the fragment instantiate its user interface view. * This is optional, and non-graphical fragments can return null (which * is the default implementation). This will be called between @@ -1280,6 +1306,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener writer.print(" mFromLayout="); writer.print(mFromLayout); writer.print(" mInLayout="); writer.println(mInLayout); writer.print(prefix); writer.print("mHidden="); writer.print(mHidden); + writer.print(" mDetached="); writer.print(mDetached); writer.print(" mRetainInstance="); writer.print(mRetainInstance); writer.print(" mRetaining="); writer.print(mRetaining); writer.print(" mHasMenu="); writer.println(mHasMenu); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index ab60cf016ba9..0da656fb6419 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -714,13 +714,14 @@ final class FragmentManagerImpl extends FragmentManager { null, f.mSavedFragmentState); if (f.mView != null) { f.mView.setSaveFromParentEnabled(false); + if (f.mHidden) f.mView.setVisibility(View.GONE); f.restoreViewState(); - if (f.mHidden) f.mView.setVisibility(View.GONE); + f.onViewCreated(f.mView, f.mSavedFragmentState); } } case Fragment.CREATED: if (newState > Fragment.CREATED) { - if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f); + if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); if (!f.mFromLayout) { ViewGroup container = null; if (f.mContainerId != 0) { @@ -744,9 +745,10 @@ final class FragmentManagerImpl extends FragmentManager { anim.start(); } container.addView(f.mView); - f.restoreViewState(); } - if (f.mHidden) f.mView.setVisibility(View.GONE); + if (f.mHidden) f.mView.setVisibility(View.GONE); + f.restoreViewState(); + f.onViewCreated(f.mView, f.mSavedFragmentState); } } @@ -756,10 +758,13 @@ final class FragmentManagerImpl extends FragmentManager { throw new SuperNotCalledException("Fragment " + f + " did not call through to super.onActivityCreated()"); } + if (f.mView != null) { + } f.mSavedFragmentState = null; } case Fragment.ACTIVITY_CREATED: - if (newState > Fragment.ACTIVITY_CREATED) { + case Fragment.STOPPED: + if (newState > Fragment.STOPPED) { if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); f.mCalled = false; f.onStart(); @@ -803,9 +808,10 @@ final class FragmentManagerImpl extends FragmentManager { + " did not call through to super.onStop()"); } } + case Fragment.STOPPED: case Fragment.ACTIVITY_CREATED: if (newState < Fragment.ACTIVITY_CREATED) { - if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); + if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); if (f.mView != null) { // Need to save the current view state if not // done already. @@ -971,32 +977,36 @@ final class FragmentManagerImpl extends FragmentManager { if (mAdded == null) { mAdded = new ArrayList<Fragment>(); } - mAdded.add(fragment); - makeActive(fragment); if (DEBUG) Log.v(TAG, "add: " + fragment); - fragment.mAdded = true; - fragment.mRemoving = false; - if (fragment.mHasMenu) { - mNeedMenuInvalidate = true; - } - if (moveToStateNow) { - moveToState(fragment); + makeActive(fragment); + if (!fragment.mDetached) { + mAdded.add(fragment); + fragment.mAdded = true; + fragment.mRemoving = false; + if (fragment.mHasMenu) { + mNeedMenuInvalidate = true; + } + if (moveToStateNow) { + moveToState(fragment); + } } } public void removeFragment(Fragment fragment, int transition, int transitionStyle) { if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); - mAdded.remove(fragment); - final boolean inactive = fragment.mBackStackNesting <= 0; - if (fragment.mHasMenu) { - mNeedMenuInvalidate = true; - } - fragment.mAdded = false; - fragment.mRemoving = true; - moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, - transition, transitionStyle); - if (inactive) { - makeInactive(fragment); + final boolean inactive = !fragment.isInBackStack(); + if (!fragment.mDetached || inactive) { + mAdded.remove(fragment); + if (fragment.mHasMenu) { + mNeedMenuInvalidate = true; + } + fragment.mAdded = false; + fragment.mRemoving = true; + moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, + transition, transitionStyle); + if (inactive) { + makeInactive(fragment); + } } } @@ -1052,6 +1062,39 @@ final class FragmentManagerImpl extends FragmentManager { } } + public void detachFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "detach: " + fragment); + if (!fragment.mDetached) { + fragment.mDetached = true; + if (fragment.mAdded) { + // We are not already in back stack, so need to remove the fragment. + mAdded.remove(fragment); + if (fragment.mHasMenu) { + mNeedMenuInvalidate = true; + } + fragment.mAdded = false; + fragment.mRemoving = true; + moveToState(fragment, Fragment.CREATED, transition, transitionStyle); + } + } + } + + public void attachFragment(Fragment fragment, int transition, int transitionStyle) { + if (DEBUG) Log.v(TAG, "attach: " + fragment); + if (fragment.mDetached) { + fragment.mDetached = false; + if (!fragment.mAdded) { + mAdded.add(fragment); + fragment.mAdded = true; + fragment.mRemoving = false; + if (fragment.mHasMenu) { + mNeedMenuInvalidate = true; + } + moveToState(fragment, mCurState, transition, transitionStyle); + } + } + } + public Fragment findFragmentById(int id) { if (mActive != null) { // First look through added fragments. @@ -1594,7 +1637,7 @@ final class FragmentManagerImpl extends FragmentManager { } public void dispatchStop() { - moveToState(Fragment.ACTIVITY_CREATED, false); + moveToState(Fragment.STOPPED, false); } public void dispatchDestroy() { diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index 0cc774d3d2f9..15b873b20c66 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -87,6 +87,31 @@ public abstract class FragmentTransaction { public abstract FragmentTransaction show(Fragment fragment); /** + * Detach the given fragment from the UI. This is the same state as + * when it is put on the back stack: the fragment is removed from + * the UI, however its state is still being actively managed by the + * fragment manager. When going into this state its view hierarchy + * is destroyed. + * + * @param fragment The fragment to be detached. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction detach(Fragment fragment); + + /** + * Re-attach a fragment after it had previously been deatched from + * the UI with {@link #detach(Fragment)}. This + * causes its view hierarchy to be re-created, attached to the UI, + * and displayed. + * + * @param fragment The fragment to be attached. + * + * @return Returns the same FragmentTransaction instance. + */ + public abstract FragmentTransaction attach(Fragment fragment); + + /** * @return <code>true</code> if this transaction contains no operations, * <code>false</code> otherwise. */ diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java index 6e2f4b6b54d3..a5ee26c9a22f 100644 --- a/core/java/android/app/ListFragment.java +++ b/core/java/android/app/ListFragment.java @@ -195,11 +195,11 @@ public class ListFragment extends Fragment { } /** - * Attach to list view once Fragment is ready to run. + * Attach to list view once the view hierarchy has been created. */ @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); ensureList(); } |