summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2011-04-15 19:00:20 -0700
committerDianne Hackborn <hackbod@google.com>2011-05-13 12:42:22 -0700
commit16f6e89c2a4bbf73fe15cb2e81c8fec98c7ac831 (patch)
tree41fa80ed8d08aa627ac827c0adbc124d11a2f29a
parent0ed2e845db3f6bc7bc12a08ada18363942c2fb3d (diff)
downloadframeworks_base-16f6e89c2a4bbf73fe15cb2e81c8fec98c7ac831.zip
frameworks_base-16f6e89c2a4bbf73fe15cb2e81c8fec98c7ac831.tar.gz
frameworks_base-16f6e89c2a4bbf73fe15cb2e81c8fec98c7ac831.tar.bz2
DO NOT MERGE. Integrate fragment work from master
Back-port new fragment detach APIs from support lib. This allow a much cleaner implementation of things like the fragment pager class. Integrate from support lib: fix restore of list state. The FragmentManager/ListFragment impl was restoring the list state before setting its adapter. This caused the list view to lose the state, since it gets cleared as part of setting the adapter. Now the fragment manager waits on restoring the view hierarchy state until after it has done onActivityCreated(), at which point we have set the adapter. It would be nice to make list view less fragile in this regard, but that is for a different change. Change-Id: I38606ef7d0b06478995f3fb7726aead67420e172
-rw-r--r--api/current.xml41
-rw-r--r--core/java/android/app/BackStackRecord.java48
-rw-r--r--core/java/android/app/Fragment.java37
-rw-r--r--core/java/android/app/FragmentManager.java97
-rw-r--r--core/java/android/app/FragmentTransaction.java25
-rw-r--r--core/java/android/app/ListFragment.java6
6 files changed, 219 insertions, 35 deletions
diff --git a/api/current.xml b/api/current.xml
index b2330be..3843f67 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 1d217f0..09e3d76 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 53dc7c8..dd158f9 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 ab60cf0..0da656f 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 0cc774d..15b873b 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 6e2f4b6..a5ee26c 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();
}