Further work on fragments:

- Implement all of the state saving and restoring machinery.  This
  caused some flux in the API.
- Add ability to have fragments that are retained across activity
  instances.
- Fix some bugs.

Change-Id: Ib6b5b0752d7f8d667cfdcd3e76d127cc9b6d901b
This commit is contained in:
Dianne Hackborn
2010-05-12 18:59:50 -07:00
parent bdfb5312c0
commit b4bc78b16a
10 changed files with 1145 additions and 293 deletions

View File

@@ -19904,6 +19904,19 @@
<parameter name="id" type="int">
</parameter>
</method>
<method name="findFragmentByTag"
return="android.app.Fragment"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
<method name="findViewById"
return="android.view.View"
abstract="false"
@@ -25414,6 +25427,19 @@
visibility="public"
>
</constructor>
<method name="equals"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
<parameter name="o" type="java.lang.Object">
</parameter>
</method>
<method name="getActivity"
return="android.app.Activity"
abstract="false"
@@ -25425,6 +25451,61 @@
visibility="public"
>
</method>
<method name="getId"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getRetainInstance"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getTag"
return="java.lang.String"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getView"
return="android.view.View"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="hashCode"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="onAttach"
return="void"
abstract="false"
@@ -25493,6 +25574,8 @@
</parameter>
<parameter name="container" type="android.view.ViewGroup">
</parameter>
<parameter name="savedInstanceState" type="android.os.Bundle">
</parameter>
</method>
<method name="onDestroy"
return="void"
@@ -25553,7 +25636,7 @@
visibility="public"
>
</method>
<method name="onRestoreInstanceState"
<method name="onReady"
return="void"
abstract="false"
native="false"
@@ -25577,17 +25660,6 @@
visibility="public"
>
</method>
<method name="onRetainNonConfigurationInstance"
return="java.lang.Object"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="onSaveInstanceState"
return="void"
abstract="false"
@@ -25623,6 +25695,19 @@
visibility="public"
>
</method>
<method name="setRetainInstance"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="retain" type="boolean">
</parameter>
</method>
</class>
<interface name="FragmentTransaction"
abstract="true"
@@ -25643,6 +25728,8 @@
>
<parameter name="fragment" type="android.app.Fragment">
</parameter>
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
<method name="add"
return="android.app.FragmentTransaction"
@@ -181501,6 +181588,17 @@
visibility="public"
>
</method>
<method name="isSaveFromParentEnabled"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isScrollbarFadingEnabled"
return="boolean"
abstract="false"
@@ -182979,6 +183077,19 @@
<parameter name="enabled" type="boolean">
</parameter>
</method>
<method name="setSaveFromParentEnabled"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="enabled" type="boolean">
</parameter>
</method>
<method name="setScrollBarStyle"
return="void"
abstract="false"

View File

@@ -42,6 +42,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -77,6 +78,9 @@ import android.widget.LinearLayout;
import com.android.internal.app.SplitActionBar;
import com.android.internal.policy.PolicyManager;
import java.util.ArrayList;
import java.util.HashMap;
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -611,6 +615,7 @@ public class Activity extends ContextThemeWrapper
private static long sInstanceCount = 0;
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
private static final String FRAGMENTS_TAG = "android:fragments";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
@@ -632,8 +637,6 @@ public class Activity extends ContextThemeWrapper
private ComponentName mComponent;
/*package*/ ActivityInfo mActivityInfo;
/*package*/ ActivityThread mMainThread;
/*package*/ Object mLastNonConfigurationInstance;
/*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances;
Activity mParent;
boolean mCalled;
private boolean mResumed;
@@ -646,6 +649,13 @@ public class Activity extends ContextThemeWrapper
/*package*/ Configuration mCurrentConfig;
private SearchManager mSearchManager;
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
ArrayList<Fragment> fragments;
}
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
private Window mWindow;
private WindowManager mWindowManager;
@@ -660,148 +670,6 @@ public class Activity extends ContextThemeWrapper
final FragmentManager mFragments = new FragmentManager();
private final Object[] sConstructorArgs = new Object[0];
private static final Class[] sConstructorSignature = new Class[] { };
private static final HashMap<String, Constructor> sConstructorMap =
new HashMap<String, Constructor>();
private final class FragmentTransactionImpl implements FragmentTransaction,
Runnable, BackStackState {
ArrayList<Fragment> mAdded;
ArrayList<Fragment> mRemoved;
int mTransition;
int mTransitionStyle;
boolean mAddToBackStack;
String mName;
boolean mCommitted;
public FragmentTransaction add(Fragment fragment) {
return add(fragment, 0);
}
public FragmentTransaction add(Fragment fragment, int containerViewId) {
if (fragment.mActivity != null) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
if (mRemoved != null) {
mRemoved.remove(fragment);
}
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
fragment.mContainerId = containerViewId;
mAdded.add(fragment);
return this;
}
public FragmentTransaction replace(Fragment fragment, int containerViewId) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
if (mFragments.mFragments != null) {
for (int i=0; i<mFragments.mFragments.size(); i++) {
Fragment old = mFragments.mFragments.get(i);
if (old.mContainerId == containerViewId) {
remove(old);
}
}
}
return add(fragment, containerViewId);
}
public FragmentTransaction remove(Fragment fragment) {
if (fragment.mActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
if (mAdded != null) {
mAdded.remove(fragment);
}
if (mRemoved == null) {
mRemoved = new ArrayList<Fragment>();
}
mRemoved.add(fragment);
return this;
}
public FragmentTransaction setTransition(int transition) {
mTransition = transition;
return this;
}
public FragmentTransaction setTransitionStyle(int styleRes) {
mTransitionStyle = styleRes;
return this;
}
public FragmentTransaction addToBackStack(String name) {
mAddToBackStack = true;
mName = name;
return this;
}
public void commit() {
if (mCommitted) throw new IllegalStateException("commit already called");
mCommitted = true;
mHandler.post(this);
}
public void run() {
if (mRemoved != null) {
for (int i=mRemoved.size()-1; i>=0; i--) {
mFragments.removeFragment(mRemoved.get(i), mTransition,
mTransitionStyle);
}
}
if (mAdded != null) {
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
mFragments.addFragment(f, false);
if (mAddToBackStack) {
f.mBackStackNesting++;
}
}
}
mFragments.moveToState(mFragments.mCurState, mTransition,
mTransitionStyle, true);
if (mAddToBackStack) {
mFragments.addBackStackState(this);
}
}
public void popFromBackStack() {
if (mAdded != null) {
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
if (mAddToBackStack) {
f.mBackStackNesting--;
}
mFragments.removeFragment(f,
FragmentManager.reverseTransit(mTransition),
mTransitionStyle);
}
}
if (mRemoved != null) {
for (int i=mRemoved.size()-1; i>=0; i--) {
mFragments.addFragment(mRemoved.get(i), false);
}
}
}
public String getName() {
return mName;
}
public int getTransition() {
return mTransition;
}
public int getTransitionStyle() {
return mTransitionStyle;
}
}
private static final class ManagedCursor {
ManagedCursor(Cursor cursor) {
mCursor = cursor;
@@ -828,7 +696,7 @@ public class Activity extends ContextThemeWrapper
protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
private Thread mUiThread;
private final Handler mHandler = new Handler();
final Handler mHandler = new Handler();
// Used for debug only
/*
@@ -952,7 +820,12 @@ public class Activity extends ContextThemeWrapper
protected void onCreate(Bundle savedInstanceState) {
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchCreate(savedInstanceState);
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
mCalled = true;
}
@@ -1237,6 +1110,10 @@ public class Activity extends ContextThemeWrapper
*/
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
}
/**
@@ -1541,7 +1418,8 @@ public class Activity extends ContextThemeWrapper
* {@link #onRetainNonConfigurationInstance()}.
*/
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstance;
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
/**
@@ -1597,8 +1475,9 @@ public class Activity extends ContextThemeWrapper
* @return Returns the object previously returned by
* {@link #onRetainNonConfigurationChildInstances()}
*/
HashMap<String,Object> getLastNonConfigurationChildInstances() {
return mLastNonConfigurationChildInstances;
HashMap<String, Object> getLastNonConfigurationChildInstances() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.children : null;
}
/**
@@ -1612,6 +1491,21 @@ public class Activity extends ContextThemeWrapper
return null;
}
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
ArrayList<Fragment> fragments = mFragments.retainNonConfig();
if (activity == null && children == null && fragments == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
return nci;
}
public void onLowMemory() {
mCalled = true;
}
@@ -1621,7 +1515,7 @@ public class Activity extends ContextThemeWrapper
* this activity.
*/
public FragmentTransaction openFragmentTransaction() {
return new FragmentTransactionImpl();
return new BackStackEntry(mFragments);
}
/**
@@ -1810,16 +1704,6 @@ public class Activity extends ContextThemeWrapper
return mActionBar;
}
/**
* Finds a fragment that was identified by the given id either when inflated
* from XML or as the container ID when added in a transaction. This only
* returns fragments that are currently added to the activity's content.
* @return The fragment if found or null otherwise.
*/
public Fragment findFragmentById(int id) {
return mFragments.findFragmentById(id);
}
/**
* Creates a new ActionBar, locates the inflated ActionBarView,
* initializes the ActionBar with the view, and sets mActionBar.
@@ -1841,6 +1725,26 @@ public class Activity extends ContextThemeWrapper
}
}
/**
* Finds a fragment that was identified by the given id either when inflated
* from XML or as the container ID when added in a transaction. This only
* returns fragments that are currently added to the activity's content.
* @return The fragment if found or null otherwise.
*/
public Fragment findFragmentById(int id) {
return mFragments.findFragmentById(id);
}
/**
* Finds a fragment that was identified by the given tag either when inflated
* from XML or as supplied when added in a transaction. This only
* returns fragments that are currently added to the activity's content.
* @return The fragment if found or null otherwise.
*/
public Fragment findFragmentByTag(String tag) {
return mFragments.findFragmentByTag(tag);
}
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
@@ -2198,6 +2102,11 @@ public class Activity extends ContextThemeWrapper
}
public void onContentChanged() {
// First time content is available, let the fragment manager
// attach all of the fragments to it.
if (mFragments.mCurState < Fragment.CONTENT) {
mFragments.moveToState(Fragment.CONTENT, false);
}
}
/**
@@ -3960,44 +3869,42 @@ public class Activity extends ContextThemeWrapper
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
String fname = a.getString(com.android.internal.R.styleable.Fragment_name);
int id = a.getInt(com.android.internal.R.styleable.Fragment_id, 0);
int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, 0);
String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
a.recycle();
Constructor constructor = sConstructorMap.get(fname);
Class clazz = null;
if (id == 0) {
throw new IllegalArgumentException(attrs.getPositionDescription()
+ ": Must specify unique android:id for " + fname);
}
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = getClassLoader().loadClass(fname);
constructor = clazz.getConstructor(sConstructorSignature);
sConstructorMap.put(fname, constructor);
// If we restored from a previous state, we may already have
// instantiated this fragment from the state and should use
// that instance instead of making a new one.
Fragment fragment = mFragments.findFragmentById(id);
if (fragment == null) {
fragment = Fragment.instantiate(this, fname);
fragment.mFromLayout = true;
fragment.mFragmentId = id;
fragment.mTag = tag;
fragment.onInflate(this, attrs);
mFragments.addFragment(fragment, true);
}
Fragment fragment = (Fragment)constructor.newInstance(sConstructorArgs);
fragment.onInflate(this, attrs);
mFragments.addFragment(fragment, true);
if (fragment.mView == null) {
throw new IllegalStateException("Fragment " + fname
+ " did not create a view.");
}
fragment.mView.setId(id);
if (fragment.mView.getTag() == null) {
fragment.mView.setTag(tag);
}
return fragment.mView;
} catch (NoSuchMethodException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + fname);
ie.initCause(e);
throw ie;
} catch (ClassNotFoundException e) {
// If loadClass fails, we should propagate the exception.
throw new RuntimeException(e);
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class "
+ (clazz == null ? "<unknown>" : clazz.getName()));
+ ": Error inflating fragment " + fname);
ie.initCause(e);
throw new RuntimeException(ie);
throw ie;
}
}
@@ -4009,18 +3916,17 @@ public class Activity extends ContextThemeWrapper
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
Application application, Intent intent, ActivityInfo info, CharSequence title,
Activity parent, String id, Object lastNonConfigurationInstance,
Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
lastNonConfigurationInstance, null, config);
lastNonConfigurationInstances, config);
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance,
HashMap<String,Object> lastNonConfigurationChildInstances,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attachBaseContext(context);
@@ -4045,8 +3951,7 @@ public class Activity extends ContextThemeWrapper
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstance = lastNonConfigurationInstance;
mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if (mParent != null) {
@@ -4104,7 +4009,7 @@ public class Activity extends ContextThemeWrapper
final void performResume() {
performRestart();
mLastNonConfigurationInstance = null;
mLastNonConfigurationInstances = null;
// First call onResume() -before- setting mResumed, so we don't
// send out any status bar / menu notifications the client makes.

View File

@@ -1300,8 +1300,7 @@ public final class ActivityThread {
Window window;
Activity parent;
String embeddedID;
Object lastNonConfigurationInstance;
HashMap<String,Object> lastNonConfigurationChildInstances;
Activity.NonConfigurationInstances lastNonConfigurationInstances;
boolean paused;
boolean stopped;
boolean hideForNow;
@@ -2479,7 +2478,7 @@ public final class ActivityThread {
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Object lastNonConfigurationInstance) {
Activity.NonConfigurationInstances lastNonConfigurationInstances) {
ActivityRecord r = new ActivityRecord();
r.token = token;
r.ident = 0;
@@ -2488,7 +2487,7 @@ public final class ActivityThread {
r.parent = parent;
r.embeddedID = id;
r.activityInfo = activityInfo;
r.lastNonConfigurationInstance = lastNonConfigurationInstance;
r.lastNonConfigurationInstances = lastNonConfigurationInstances;
if (localLOGV) {
ComponentName compname = intent.getComponent();
String name;
@@ -2610,14 +2609,12 @@ public final class ActivityThread {
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
r.embeddedID, r.lastNonConfigurationInstances, config);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstance = null;
r.lastNonConfigurationChildInstances = null;
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
@@ -3618,8 +3615,8 @@ public final class ActivityThread {
}
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstance
= r.activity.onRetainNonConfigurationInstance();
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -3628,18 +3625,6 @@ public final class ActivityThread {
+ ": " + e.toString(), e);
}
}
try {
r.lastNonConfigurationChildInstances
= r.activity.onRetainNonConfigurationChildInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain child activities "
+ safeToComponentShortString(r.intent)
+ ": " + e.toString(), e);
}
}
}
try {
r.activity.mCalled = false;

View File

@@ -17,46 +17,281 @@
package android.app;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
final class FragmentState implements Parcelable {
static final String VIEW_STATE_TAG = "android:view_state";
final String mClassName;
final boolean mFromLayout;
final int mSavedStateId;
final int mFragmentId;
final int mContainerId;
final String mTag;
final boolean mRetainInstance;
Bundle mSavedFragmentState;
Fragment mInstance;
public FragmentState(Fragment frag) {
mClassName = frag.getClass().getName();
mFromLayout = frag.mFromLayout;
mSavedStateId = frag.mSavedStateId;
mFragmentId = frag.mFragmentId;
mContainerId = frag.mContainerId;
mTag = frag.mTag;
mRetainInstance = frag.mRetainInstance;
}
public FragmentState(Parcel in) {
mClassName = in.readString();
mFromLayout = in.readInt() != 0;
mSavedStateId = in.readInt();
mFragmentId = in.readInt();
mContainerId = in.readInt();
mTag = in.readString();
mRetainInstance = in.readInt() != 0;
mSavedFragmentState = in.readBundle();
}
public Fragment instantiate(Activity activity) {
if (mFromLayout) {
return null;
}
if (mInstance != null) {
return mInstance;
}
try {
mInstance = Fragment.instantiate(activity, mClassName);
} catch (Exception e) {
throw new RuntimeException("Unable to restore fragment " + mClassName, e);
}
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(activity.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
mInstance.mSavedViewState
= mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG);
}
mInstance.mSavedStateId = mSavedStateId;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
return mInstance;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mClassName);
dest.writeInt(mFromLayout ? 1 : 0);
dest.writeInt(mSavedStateId);
dest.writeInt(mFragmentId);
dest.writeInt(mContainerId);
dest.writeString(mTag);
dest.writeInt(mRetainInstance ? 1 : 0);
dest.writeBundle(mSavedFragmentState);
}
public static final Parcelable.Creator<FragmentState> CREATOR
= new Parcelable.Creator<FragmentState>() {
public FragmentState createFromParcel(Parcel in) {
return new FragmentState(in);
}
public FragmentState[] newArray(int size) {
return new FragmentState[size];
}
};
}
/**
* A Fragment is a piece of an application's user interface or behavior
* that can be placed in an {@link Activity}.
*/
public class Fragment implements ComponentCallbacks {
private static final Object[] sConstructorArgs = new Object[0];
private static final Class[] sConstructorSignature = new Class[] { };
private static final HashMap<String, Constructor> sConstructorMap =
new HashMap<String, Constructor>();
static final int INITIALIZING = 0; // Not yet created.
static final int CREATED = 1; // Created.
static final int STARTED = 2; // Created and started, not resumed.
static final int RESUMED = 3; // Created started and resumed.
static final int CONTENT = 2; // View hierarchy content available.
static final int STARTED = 3; // Created and started, not resumed.
static final int RESUMED = 4; // Created started and resumed.
int mState = INITIALIZING;
// When instantiated from saved state, this is the saved state.
Bundle mSavedFragmentState;
SparseArray<Parcelable> mSavedViewState;
// Set to true if this fragment was instantiated from a layout file.
boolean mFromLayout;
// Number of active back stack entries this fragment is in.
int mBackStackNesting;
// Activity this fragment is attached to.
Activity mActivity;
boolean mCalled;
// The optional identifier for this fragment -- either the container ID if it
// was dynamically added to the view hierarchy, or the ID supplied in
// layout.
int mFragmentId;
// When a fragment is being dynamically added to the view hierarchy, this
// is the identifier of the parent container it is being added to.
int mContainerId;
// The optional named tag for this fragment -- usually used to find
// fragments that are not part of the layout.
String mTag;
// If set this fragment would like its instance retained across
// configuration changes.
boolean mRetainInstance;
// If set this fragment is being retained across the current config change.
boolean mRetaining;
// Used to verify that subclasses call through to super class.
boolean mCalled;
// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
// The View generated for this fragment.
View mView;
// Used for performing save state of fragments.
int mSavedStateSeq = 0;
int mSavedStateId;
public Fragment() {
}
static Fragment instantiate(Activity activity, String fname)
throws NoSuchMethodException, ClassNotFoundException,
IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Constructor constructor = sConstructorMap.get(fname);
Class clazz = null;
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = activity.getClassLoader().loadClass(fname);
constructor = clazz.getConstructor(sConstructorSignature);
sConstructorMap.put(fname, constructor);
}
return (Fragment)constructor.newInstance(sConstructorArgs);
}
void restoreViewState() {
if (mSavedViewState != null) {
mView.restoreHierarchyState(mSavedViewState);
mSavedViewState = null;
}
}
/**
* Subclasses can not override equals().
*/
@Override final public boolean equals(Object o) {
return super.equals(o);
}
/**
* Subclasses can not override hashCode().
*/
@Override final public int hashCode() {
return super.hashCode();
}
/**
* Return the identifier this fragment is known by. This is either
* the android:id value supplied in a layout or the container view ID
* supplied when adding the fragment.
*/
public int getId() {
return mFragmentId;
}
/**
* Get the tag name of the fragment, if specified.
*/
public String getTag() {
return mTag;
}
/**
* Return the Activity this fragment is currently associated with.
*/
public Activity getActivity() {
return mActivity;
}
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
* <ul>
* <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
public void setRetainInstance(boolean retain) {
mRetainInstance = retain;
}
public boolean getRetainInstance() {
return mRetainInstance;
}
/**
* Called when a fragment is being created as part of a view layout
* inflation, typically from setting the content view of an activity.
*
* @param activity The Activity that is inflating the fragment.
* @param attrs The attributes at the tag where the fragment is
* being created.
*/
public void onInflate(Activity activity, AttributeSet attrs) {
mCalled = true;
}
/**
* Called when a fragment is first attached to its activity.
* {@link #onCreate(Bundle)} will be called after this.
*/
public void onAttach(Activity activity) {
mCalled = true;
}
@@ -65,21 +300,70 @@ public class Fragment implements ComponentCallbacks {
return null;
}
/**
* Called to do initial creation of a fragment. This is called after
* {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}.
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
public void onCreate(Bundle savedInstanceState) {
mCalled = true;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container) {
/**
* 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
* {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}.
*
* @param inflater The LayoutInflater object that can be used to inflate
* any views in the fragment,
* @param container If non-null, this is the parent view that the fragment's
* UI should be attached to. The fragment should not add the view itself,
* but this can be used to generate the LayoutParams of the view.
* @param savedInstanceState If non-null, this fragment is being re-constructed
* from a previous saved state as given here.
*
* @return Return the View for the fragment's UI, or null.
*/
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return null;
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
public View getView() {
return mView;
}
/**
* Called when the activity is ready for the fragment to run. This is
* most useful for fragments that use {@link #setRetainInstance(boolean)}
* instance, as this tells the fragment when it is fully associated with
* the new activity instance. This is called after {@link #onCreate(Bundle)}
* and before {@link #onStart()}.
*
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
public void onReady(Bundle savedInstanceState) {
mCalled = true;
}
/**
* Called when the Fragment is visible to the user. This is generally
* tied to {@link Activity#onStart() Activity.onStart} of the containing
* Activity's lifecycle.
*/
public void onStart() {
mCalled = true;
}
/**
* Called when the fragment is visible to the user and actively running.
* This is generally
* tied to {@link Activity#onResume() Activity.onResume} of the containing
* Activity's lifecycle.
*/
public void onResume() {
mCalled = true;
}
@@ -91,14 +375,20 @@ public class Fragment implements ComponentCallbacks {
mCalled = true;
}
public Object onRetainNonConfigurationInstance() {
return null;
}
/**
* Called when the Fragment is no longer resumed. This is generally
* tied to {@link Activity#onPause() Activity.onPause} of the containing
* Activity's lifecycle.
*/
public void onPause() {
mCalled = true;
}
/**
* Called when the Fragment is no longer started. This is generally
* tied to {@link Activity#onStop() Activity.onStop} of the containing
* Activity's lifecycle.
*/
public void onStop() {
mCalled = true;
}
@@ -107,10 +397,18 @@ public class Fragment implements ComponentCallbacks {
mCalled = true;
}
/**
* Called when the fragment is no longer in use. This is called
* after {@link #onStop()} and before {@link #onDetach()}.
*/
public void onDestroy() {
mCalled = true;
}
/**
* Called when the fragment is no longer attached to its activity. This
* is called after {@link #onDestroy()}.
*/
public void onDetach() {
mCalled = true;
}

View File

@@ -19,17 +19,269 @@ package android.app;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.util.ArrayList;
import java.util.HashMap;
interface BackStackState {
public void popFromBackStack();
public String getName();
public int getTransition();
public int getTransitionStyle();
final class BackStackEntry implements FragmentTransaction, Runnable {
final FragmentManager mManager;
ArrayList<Fragment> mAdded;
ArrayList<Fragment> mRemoved;
int mTransition;
int mTransitionStyle;
boolean mAddToBackStack;
String mName;
boolean mCommitted;
public BackStackEntry(FragmentManager manager) {
mManager = manager;
}
public FragmentTransaction add(Fragment fragment, String tag) {
fragment.mTag = tag;
return add(fragment, 0);
}
public FragmentTransaction add(Fragment fragment, int containerViewId) {
if (fragment.mActivity != null) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
if (mRemoved != null) {
mRemoved.remove(fragment);
}
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
mAdded.add(fragment);
return this;
}
public FragmentTransaction replace(Fragment fragment, int containerViewId) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
if (mManager.mFragments != null) {
for (int i=0; i<mManager.mFragments.size(); i++) {
Fragment old = mManager.mFragments.get(i);
if (old.mContainerId == containerViewId) {
remove(old);
}
}
}
return add(fragment, containerViewId);
}
public FragmentTransaction remove(Fragment fragment) {
if (fragment.mActivity == null) {
throw new IllegalStateException("Fragment not added: " + fragment);
}
if (mAdded != null) {
mAdded.remove(fragment);
}
if (mRemoved == null) {
mRemoved = new ArrayList<Fragment>();
}
mRemoved.add(fragment);
return this;
}
public FragmentTransaction setTransition(int transition) {
mTransition = transition;
return this;
}
public FragmentTransaction setTransitionStyle(int styleRes) {
mTransitionStyle = styleRes;
return this;
}
public FragmentTransaction addToBackStack(String name) {
mAddToBackStack = true;
mName = name;
return this;
}
public void commit() {
if (mCommitted) throw new IllegalStateException("commit already called");
mCommitted = true;
mManager.mActivity.mHandler.post(this);
}
public void run() {
if (mRemoved != null) {
for (int i=mRemoved.size()-1; i>=0; i--) {
mManager.removeFragment(mRemoved.get(i), mTransition,
mTransitionStyle);
}
}
if (mAdded != null) {
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
mManager.addFragment(f, false);
if (mAddToBackStack) {
f.mBackStackNesting++;
}
}
}
mManager.moveToState(mManager.mCurState, mTransition,
mTransitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
public void popFromBackStack() {
if (mAdded != null) {
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
if (mAddToBackStack) {
f.mBackStackNesting--;
}
mManager.removeFragment(f,
FragmentManager.reverseTransit(mTransition),
mTransitionStyle);
}
}
if (mRemoved != null) {
for (int i=mRemoved.size()-1; i>=0; i--) {
mManager.addFragment(mRemoved.get(i), false);
}
}
}
public String getName() {
return mName;
}
public int getTransition() {
return mTransition;
}
public int getTransitionStyle() {
return mTransitionStyle;
}
}
final class BackStackState implements Parcelable {
final int[] mAdded;
final int[] mRemoved;
final int mTransition;
final int mTransitionStyle;
final String mName;
public BackStackState(FragmentManager fm, BackStackEntry bse) {
mAdded = buildFragmentStateList(fm, bse.mAdded);
mRemoved = buildFragmentStateList(fm, bse.mRemoved);
mTransition = bse.mTransition;
mTransitionStyle = bse.mTransitionStyle;
mName = bse.mName;
}
public BackStackState(Parcel in) {
mAdded = in.createIntArray();
mRemoved = in.createIntArray();
mTransition = in.readInt();
mTransitionStyle = in.readInt();
mName = in.readString();
}
public BackStackEntry instantiate(FragmentManager fm) {
BackStackEntry bse = new BackStackEntry(fm);
bse.mAdded = buildFragmentList(fm, mAdded);
bse.mRemoved = buildFragmentList(fm, mRemoved);
bse.mTransition = mTransition;
bse.mTransitionStyle = mTransitionStyle;
bse.mName = mName;
return bse;
}
static int[] buildFragmentStateList(FragmentManager fm, ArrayList<Fragment> frags) {
if (frags == null) return null;
final int N = frags.size();
int[] ids = new int[N];
for (int i=0; i<N; i++) {
FragmentState fs = fm.saveFragment(frags.get(i));
ids[i] = fs.mSavedStateId;
}
return ids;
}
static ArrayList<Fragment> buildFragmentList(FragmentManager fm, int[] states) {
if (states == null) return null;
final int N = states.length;
ArrayList<Fragment> frags = new ArrayList<Fragment>(N);
for (int i=0; i<N; i++) {
frags.add(fm.mRestoredFragments.get(states[i]).instantiate(fm.mActivity));
}
return frags;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeIntArray(mAdded);
dest.writeIntArray(mRemoved);
dest.writeInt(mTransition);
dest.writeInt(mTransitionStyle);
dest.writeString(mName);
}
public static final Parcelable.Creator<BackStackState> CREATOR
= new Parcelable.Creator<BackStackState>() {
public BackStackState createFromParcel(Parcel in) {
return new BackStackState(in);
}
public BackStackState[] newArray(int size) {
return new BackStackState[size];
}
};
}
final class FragmentManagerState implements Parcelable {
FragmentState[] mFragments;
int[] mAdded;
BackStackState[] mBackStack;
public FragmentManagerState() {
}
public FragmentManagerState(Parcel in) {
mFragments = in.createTypedArray(FragmentState.CREATOR);
mAdded = in.createIntArray();
mBackStack = in.createTypedArray(BackStackState.CREATOR);
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedArray(mFragments, flags);
dest.writeIntArray(mAdded);
dest.writeTypedArray(mBackStack, flags);
}
public static final Parcelable.Creator<FragmentManagerState> CREATOR
= new Parcelable.Creator<FragmentManagerState>() {
public FragmentManagerState createFromParcel(Parcel in) {
return new FragmentManagerState(in);
}
public FragmentManagerState[] newArray(int size) {
return new FragmentManagerState[size];
}
};
}
/**
@@ -38,11 +290,20 @@ interface BackStackState {
*/
public class FragmentManager {
ArrayList<Fragment> mFragments;
ArrayList<BackStackState> mBackStack;
ArrayList<BackStackEntry> mBackStack;
int mCurState = Fragment.INITIALIZING;
Activity mActivity;
int mSaveStateSeq = 0;
// Temporary vars for state save and restore.
int mCurSaveId = 0;
HashMap<Fragment, FragmentState> mSavedFragments;
SparseArray<FragmentState> mRestoredFragments;
Bundle mStateBundle = null;
SparseArray<Parcelable> mStateArray = null;
Animation loadAnimation(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animation animObj = fragment.onCreateAnimation(transitionStyle, enter);
@@ -89,32 +350,60 @@ public class FragmentManager {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
f.mCalled = false;
f.onCreate(null);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onCreate()");
}
ViewGroup container = null;
if (f.mContainerId != 0) {
container = (ViewGroup)mActivity.findViewById(f.mContainerId);
if (container == null) {
throw new IllegalArgumentException("New view found for id 0x"
+ Integer.toHexString(f.mContainerId)
+ " for fragment " + f);
if (!f.mRetaining) {
f.mCalled = false;
f.onCreate(f.mSavedFragmentState);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onCreate()");
}
}
f.mContainer = container;
f.mView = f.onCreateView(mActivity.getLayoutInflater(), container);
if (container != null && f.mView != null) {
Animation anim = loadAnimation(f, transit, true, transitionStyle);
if (anim != null) {
f.mView.setAnimation(anim);
}
container.addView(f.mView);
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.onCreateView(mActivity.getLayoutInflater(),
null, f.mSavedFragmentState);
}
case Fragment.CONTENT:
if (newState > Fragment.CONTENT) {
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
container = (ViewGroup)mActivity.findViewById(f.mContainerId);
if (container == null) {
throw new IllegalArgumentException("New view found for id 0x"
+ Integer.toHexString(f.mContainerId)
+ " for fragment " + f);
}
}
f.mContainer = container;
f.mView = f.onCreateView(mActivity.getLayoutInflater(),
container, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
Animation anim = loadAnimation(f, transit, true,
transitionStyle);
if (anim != null) {
f.mView.setAnimation(anim);
}
container.addView(f.mView);
f.restoreViewState();
}
}
}
f.mCalled = false;
f.onReady(f.mSavedFragmentState);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onReady()");
}
f.mSavedFragmentState = null;
}
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
f.mCalled = false;
@@ -154,24 +443,39 @@ public class FragmentManager {
+ " did not call through to super.onStop()");
}
}
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (f.mContainer != null) {
Animation anim = loadAnimation(f, transit, false, transitionStyle);
if (anim != null) {
f.mView.setAnimation(anim);
case Fragment.CONTENT:
if (newState < Fragment.CONTENT) {
if (f.mView != null) {
// Need to save the current view state if not
// done already.
if (!mActivity.isFinishing()) {
saveFragmentViewState(f);
}
if (f.mContainer != null) {
if (mCurState > Fragment.INITIALIZING) {
Animation anim = loadAnimation(f, transit, false,
transitionStyle);
if (anim != null) {
f.mView.setAnimation(anim);
}
}
f.mContainer.removeView(f.mView);
}
f.mContainer.removeView(f.mView);
}
f.mContainer = null;
f.mView = null;
f.mCalled = false;
f.onDestroy();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onDestroy()");
}
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (!f.mRetaining) {
f.mCalled = false;
f.onDestroy();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onDestroy()");
}
}
f.mCalled = false;
f.onDetach();
if (!f.mCalled) {
@@ -227,7 +531,7 @@ public class FragmentManager {
if (mFragments != null) {
for (int i=mFragments.size()-1; i>=0; i--) {
Fragment f = mFragments.get(i);
if (f.mContainerId == id) {
if (f.mFragmentId == id) {
return f;
}
}
@@ -235,9 +539,21 @@ public class FragmentManager {
return null;
}
public void addBackStackState(BackStackState state) {
public Fragment findFragmentByTag(String tag) {
if (mFragments != null && tag != null) {
for (int i=mFragments.size()-1; i>=0; i--) {
Fragment f = mFragments.get(i);
if (tag.equals(f.mTag)) {
return f;
}
}
}
return null;
}
public void addBackStackState(BackStackEntry state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackState>();
mBackStack = new ArrayList<BackStackEntry>();
}
mBackStack.add(state);
}
@@ -251,7 +567,7 @@ public class FragmentManager {
if (last < 0) {
return false;
}
final BackStackState bss = mBackStack.remove(last);
final BackStackEntry bss = mBackStack.remove(last);
handler.post(new Runnable() {
public void run() {
bss.popFromBackStack();
@@ -262,7 +578,7 @@ public class FragmentManager {
} else {
int index = mBackStack.size()-1;
while (index >= 0) {
BackStackState bss = mBackStack.get(index);
BackStackEntry bss = mBackStack.get(index);
if (name.equals(bss.getName())) {
break;
}
@@ -270,7 +586,8 @@ public class FragmentManager {
if (index < 0 || index == mBackStack.size()-1) {
return false;
}
final ArrayList<BackStackState> states = new ArrayList<BackStackState>();
final ArrayList<BackStackEntry> states
= new ArrayList<BackStackEntry>();
for (int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
@@ -286,12 +603,189 @@ public class FragmentManager {
return true;
}
ArrayList<Fragment> retainNonConfig() {
ArrayList<Fragment> fragments = null;
if (mFragments != null) {
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f.mBackStackNesting <= 0 && f.mRetainInstance && f.mTag != null) {
if (fragments == null) {
fragments = new ArrayList<Fragment>();
}
fragments.add(f);
f.mRetaining = true;
}
}
}
return fragments;
}
void saveFragmentViewState(Fragment f) {
if (f.mSavedViewState != null) {
return;
}
if (mStateArray == null) {
mStateArray = new SparseArray<Parcelable>();
}
f.mView.saveHierarchyState(mStateArray);
if (mStateArray.size() > 0) {
f.mSavedViewState = mStateArray;
mStateArray = null;
}
}
FragmentState saveFragment(Fragment f) {
if (mSavedFragments != null) {
FragmentState fs = mSavedFragments.get(f);
if (fs != null) {
return fs;
}
} else {
mSavedFragments = new HashMap<Fragment, FragmentState>();
}
f.mSavedStateSeq = mSaveStateSeq;
f.mSavedStateId = ++mCurSaveId;
FragmentState fs = new FragmentState(f);
mSavedFragments.put(f, fs);
if (mStateBundle == null) {
mStateBundle = new Bundle();
}
f.onSaveInstanceState(mStateBundle);
if (!mStateBundle.isEmpty()) {
fs.mSavedFragmentState = mStateBundle;
mStateBundle = null;
}
if (f.mView != null) {
saveFragmentViewState(f);
if (f.mSavedViewState != null) {
if (fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = new Bundle();
}
fs.mSavedFragmentState.putSparseParcelableArray(
FragmentState.VIEW_STATE_TAG, f.mSavedViewState);
}
}
return fs;
}
Parcelable saveAllState() {
mSavedFragments = null;
mSaveStateSeq++;
if (mFragments == null) {
return null;
}
int[] added = null;
BackStackState[] backStack = null;
// First collect all active fragments.
int N = mFragments.size();
if (N > 0) {
added = new int[N];
for (int i=0; i<N; i++) {
added[i] = saveFragment(mFragments.get(i)).mSavedStateId;
}
}
// Now save back stack.
if (mBackStack != null) {
N = mBackStack.size();
if (N > 0) {
backStack = new BackStackState[N];
for (int i=0; i<N; i++) {
backStack[i] = new BackStackState(this, mBackStack.get(i));
}
}
}
if (mSavedFragments == null) {
return null;
}
N = mSavedFragments.size();
if (N <= 0) {
return null;
}
FragmentManagerState fms = new FragmentManagerState();
fms.mFragments = new FragmentState[N];
int i = 0;
for (FragmentState fs : mSavedFragments.values()) {
fms.mFragments[i] = fs;
i++;
}
fms.mAdded = added;
fms.mBackStack = backStack;
return fms;
}
void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
// If there is no saved state at all, then there can not be
// any nonConfig fragments either, so that is that.
if (state == null) return;
FragmentManagerState fms = (FragmentManagerState)state;
if (fms.mFragments == null) return;
// First build our lookup table of all known Fragment objects.
mRestoredFragments = new SparseArray<FragmentState>();
for (int i=0; i<fms.mFragments.length; i++) {
FragmentState fs = fms.mFragments[i];
mRestoredFragments.put(fs.mSavedStateId, fs);
}
// Stick any non-config instances we are retaining directly
// into the lookup table, so we don't try to instantiate them again.
if (nonConfig != null) {
for (int i=0; i<nonConfig.size(); i++) {
Fragment f = nonConfig.get(i);
FragmentState fs = mRestoredFragments.get(f.mSavedStateId);
fs.mInstance = f;
f.mSavedViewState = null;
if (fs.mSavedFragmentState != null) {
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
FragmentState.VIEW_STATE_TAG);
}
addFragment(f, false);
}
}
// Now build our data structures from the saved state, instantiating
// the fragment objects as needed.
if (fms.mAdded != null) {
if (mFragments == null) {
mFragments = new ArrayList<Fragment>(fms.mAdded.length);
}
for (int i=0; i<fms.mAdded.length; i++) {
FragmentState fs = mRestoredFragments.get(fms.mAdded[i]);
Fragment f = fs.instantiate(mActivity);
// This will return null if this is a layout fragment,
// since the instance for such a fragment will be created
// later during layout inflation.
if (f != null) {
mFragments.add(f);
}
}
}
if (fms.mBackStack != null) {
mBackStack = new ArrayList<BackStackEntry>(fms.mBackStack.length);
for (int i=0; i<fms.mBackStack.length; i++) {
BackStackEntry bse = fms.mBackStack[i].instantiate(this);
mBackStack.add(bse);
}
}
}
public void attachActivity(Activity activity) {
if (mActivity != null) throw new IllegalStateException();
mActivity = activity;
}
public void dispatchCreate(Bundle state) {
public void dispatchCreate() {
moveToState(Fragment.CREATED, false);
}

View File

@@ -4,7 +4,7 @@ package android.app;
* API for performing a set of Fragment operations.
*/
public interface FragmentTransaction {
public FragmentTransaction add(Fragment fragment);
public FragmentTransaction add(Fragment fragment, String tag);
public FragmentTransaction add(Fragment fragment, int containerViewId);
public FragmentTransaction replace(Fragment fragment, int containerViewId);
public FragmentTransaction remove(Fragment fragment);

View File

@@ -997,8 +997,10 @@ public class Instrumentation {
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
activity.attach(context, aThread, this, token, application, intent, info, title,
parent, id, lastNonConfigurationInstance, new Configuration());
activity.attach(context, aThread, this, token, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration());
return activity;
}

View File

@@ -112,11 +112,16 @@ public class LocalActivityManager {
if (r.curState == INITIALIZING) {
// Get the lastNonConfigurationInstance for the activity
HashMap<String,Object> lastNonConfigurationInstances =
mParent.getLastNonConfigurationChildInstances();
Object instance = null;
HashMap<String, Object> lastNonConfigurationInstances =
mParent.getLastNonConfigurationChildInstances();
Object instanceObj = null;
if (lastNonConfigurationInstances != null) {
instance = lastNonConfigurationInstances.get(r.id);
instanceObj = lastNonConfigurationInstances.get(r.id);
}
Activity.NonConfigurationInstances instance = null;
if (instanceObj != null) {
instance = new Activity.NonConfigurationInstances();
instance.activity = instanceObj;
}
// We need to have always created the activity.

View File

@@ -886,6 +886,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
/**
* <p>Indicates that the view hierarchy should stop saving state when
* it reaches this view. If state saving is initiated immediately at
* the view, it will be allowed.
* {@hide}
*/
static final int PARENT_SAVE_DISABLED = 0x20000000;
/**
* <p>Mask for use with setFlags indicating bits used for PARENT_SAVE_DISABLED.</p>
* {@hide}
*/
static final int PARENT_SAVE_DISABLED_MASK = 0x20000000;
/**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
* should add all focusable Views regardless if they are focusable in touch mode.
@@ -3341,6 +3355,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
* Indicates whether the entire hierarchy under this view will save its
* state when a state saving traversal occurs from its parent. The default
* is true; if false, these views will not be saved unless
* {@link #saveHierarchyState(SparseArray)} is called directly on this view.
*
* @return Returns true if the view state saving from parent is enabled, else false.
*
* @see #setSaveFromParentEnabled(boolean)
*/
public boolean isSaveFromParentEnabled() {
return (mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED;
}
/**
* Controls whether the entire hierarchy under this view will save its
* state when a state saving traversal occurs from its parent. The default
* is true; if false, these views will not be saved unless
* {@link #saveHierarchyState(SparseArray)} is called directly on this view.
*
* @param enabled Set to false to <em>disable</em> state saving, or true
* (the default) to allow it.
*
* @see #isSaveFromParentEnabled()
* @see #setId(int)
* @see #onSaveInstanceState()
*/
public void setSaveFromParentEnabled(boolean enabled) {
setFlags(enabled ? 0 : PARENT_SAVE_DISABLED, PARENT_SAVE_DISABLED_MASK);
}
/**
* Returns whether this View is able to take focus.
*

View File

@@ -1181,7 +1181,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
children[i].dispatchSaveInstanceState(container);
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchSaveInstanceState(container);
}
}
}
@@ -1206,7 +1209,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
children[i].dispatchRestoreInstanceState(container);
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchRestoreInstanceState(container);
}
}
}