The first place where application code should generally run is in
+ * {@link #onAttach(Activity)}, which is the point where the fragment is
+ * actually attached to its activity and thus capable of doing most
* retrieve such parameters from the activity in {@link #onAttach(Activity)}.
*/
public Fragment() {
}
+ /**
+ * Like {@link #instantiate(Context, String, Bundle)} but with a null
+ * argument Bundle.
+ */
+ public static Fragment instantiate(Context context, String fname) {
+ return instantiate(context, fname, null);
+ }
+
/**
* Create a new instance of a Fragment with the given class name. This is
* the same as calling its empty constructor.
@@ -237,29 +264,40 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* @param context The calling context being used to instantiate the fragment.
* This is currently just used to get its ClassLoader.
* @param fname The class name of the fragment to instantiate.
+ * @param args Bundle of arguments to supply to the fragment, which it
+ * can retrieve with {@link #getArguments()}. May be null.
* @return Returns a new fragment instance.
- * @throws NoSuchMethodException The fragment does not have an empty constructor.
- * @throws ClassNotFoundException The fragment class does not exist.
- * @throws IllegalArgumentException Bad arguments supplied to fragment class
- * constructor (should not happen).
- * @throws InstantiationException Caller does not have permission to instantiate
- * the fragment (for example its constructor is not public).
- * @throws IllegalAccessException Caller does not have permission to access
- * the given fragment class.
- * @throws InvocationTargetException Failure running the fragment's constructor.
+ * @throws InstantiationException If there is a failure in instantiating
+ * the given fragment class. This is a runtime exception; it is not
+ * normally expected to happen.
*/
- public static Fragment instantiate(Context context, String fname)
- throws NoSuchMethodException, ClassNotFoundException,
- IllegalArgumentException, InstantiationException,
- IllegalAccessException, InvocationTargetException {
- Class> clazz = sClassMap.get(fname);
-
- if (clazz == null) {
- // Class not found in the cache, see if it's real, and try to add it
- clazz = context.getClassLoader().loadClass(fname);
- sClassMap.put(fname, clazz);
+ public static Fragment instantiate(Context context, String fname, Bundle args) {
+ try {
+ Class> clazz = sClassMap.get(fname);
+ if (clazz == null) {
+ // Class not found in the cache, see if it's real, and try to add it
+ clazz = context.getClassLoader().loadClass(fname);
+ sClassMap.put(fname, clazz);
+ }
+ Fragment f = (Fragment)clazz.newInstance();
+ if (args != null) {
+ args.setClassLoader(f.getClass().getClassLoader());
+ f.mArguments = args;
+ }
+ return f;
+ } catch (ClassNotFoundException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ } catch (java.lang.InstantiationException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
+ } catch (IllegalAccessException e) {
+ throw new InstantiationException("Unable to instantiate fragment " + fname
+ + ": make sure class name exists, is public, and has an"
+ + " empty constructor that is public", e);
}
- return (Fragment)clazz.newInstance();
}
void restoreViewState() {
@@ -330,6 +368,28 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
return mTag;
}
+ /**
+ * Supply the construction arguments for this fragment. This can only
+ * be called before the fragment has been attached to its activity; that
+ * is, you should call it immediately after constructing the fragment. The
+ * arguments supplied here will be retained across fragment destroy and
+ * creation.
+ */
+ final public void setArguments(Bundle args) {
+ if (mIndex >= 0) {
+ throw new IllegalStateException("Fragment already active");
+ }
+ mArguments = args;
+ }
+
+ /**
+ * Return the arguments supplied when the fragment was instantiated,
+ * if any.
+ */
+ final public Bundle getArguments() {
+ return mArguments;
+ }
+
/**
* Return the Activity this fragment is currently associated with.
*/
@@ -337,6 +397,14 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
return mActivity;
}
+ /**
+ * Return the FragmentManager for interacting with fragments associated
+ * with this fragment's activity.
+ */
+ final public FragmentManager getFragmentManager() {
+ return mActivity.mFragments;
+ }
+
/**
* Return true if the fragment is currently added to its activity.
*/
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 54e37b0df5738..0556f0592d1e0 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -35,6 +35,100 @@ import android.view.animation.AnimationUtils;
import java.util.ArrayList;
+/**
+ * Interface for interacting with {@link Fragment} objects inside of an
+ * {@link Activity}
+ */
+public interface FragmentManager {
+ /**
+ * Start a series of edit operations on the Fragments associated with
+ * this FragmentManager.
+ */
+ public FragmentTransaction openTransaction();
+
+ /**
+ * 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 first
+ * searches through fragments that are currently added to the manager's
+ * activity; if no such fragment is found, then all fragments currently
+ * on the back stack associated with this ID are searched.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentById(int 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 first
+ * searches through fragments that are currently added to the manager's
+ * activity; if no such fragment is found, then all fragments currently
+ * on the back stack are searched.
+ * @return The fragment if found or null otherwise.
+ */
+ public Fragment findFragmentByTag(String tag);
+
+ /**
+ * Flag for {@link #popBackStack(String, int)}
+ * and {@link #popBackStack(int, int)}: If set, and the name or ID of
+ * a back stack entry has been supplied, then all matching entries will
+ * be consumed until one that doesn't match is found or the bottom of
+ * the stack is reached. Otherwise, all entries up to but not including that entry
+ * will be removed.
+ */
+ public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
+
+ /**
+ * Pop the top state off the back stack. Returns true if there was one
+ * to pop, else false.
+ */
+ public boolean popBackStack();
+
+ /**
+ * Pop the last fragment transition from the manager's fragment
+ * back stack. If there is nothing to pop, false is returned.
+ * @param name If non-null, this is the name of a previous back state
+ * to look for; if found, all states up to that state will be popped. The
+ * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+ * the named state itself is popped. If null, only the top state is popped.
+ * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ */
+ public boolean popBackStack(String name, int flags);
+
+ /**
+ * Pop all back stack states up to the one with the given identifier.
+ * @param id Identifier of the stated to be popped. If no identifier exists,
+ * false is returned.
+ * The identifier is the number returned by
+ * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The
+ * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+ * the named state itself is popped.
+ * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+ */
+ public boolean popBackStack(int id, int flags);
+
+ /**
+ * Put a reference to a fragment in a Bundle. This Bundle can be
+ * persisted as saved state, and when later restoring
+ * {@link #getFragment(Bundle, String)} will return the current
+ * instance of the same fragment.
+ *
+ * @param bundle The bundle in which to put the fragment reference.
+ * @param key The name of the entry in the bundle.
+ * @param fragment The Fragment whose reference is to be stored.
+ */
+ public void putFragment(Bundle bundle, String key, Fragment fragment);
+
+ /**
+ * Retrieve the current Fragment instance for a reference previously
+ * placed with {@link #putFragment(Bundle, String, Fragment)}.
+ *
+ * @param bundle The bundle from which to retrieve the fragment reference.
+ * @param key The name of the entry in the bundle.
+ * @return Returns the current Fragment instance that is associated with
+ * the given reference.
+ */
+ public Fragment getFragment(Bundle bundle, String key);
+}
+
final class FragmentManagerState implements Parcelable {
FragmentState[] mActive;
int[] mAdded;
@@ -75,7 +169,7 @@ final class FragmentManagerState implements Parcelable {
* @hide
* Container for fragments associated with an activity.
*/
-public class FragmentManager {
+class FragmentManagerImpl implements FragmentManager {
static final boolean DEBUG = true;
static final String TAG = "FragmentManager";
@@ -108,6 +202,47 @@ public class FragmentManager {
}
};
+ public FragmentTransaction openTransaction() {
+ return new BackStackEntry(this);
+ }
+
+ public boolean popBackStack() {
+ return popBackStackState(mActivity.mHandler, null, -1, 0);
+ }
+
+ public boolean popBackStack(String name, int flags) {
+ return popBackStackState(mActivity.mHandler, name, -1, flags);
+ }
+
+ public boolean popBackStack(int id, int flags) {
+ if (id < 0) {
+ throw new IllegalArgumentException("Bad id: " + id);
+ }
+ return popBackStackState(mActivity.mHandler, null, id, flags);
+ }
+
+ public void putFragment(Bundle bundle, String key, Fragment fragment) {
+ if (fragment.mIndex < 0) {
+ throw new IllegalStateException("Fragment " + fragment
+ + " is not currently in the FragmentManager");
+ }
+ bundle.putInt(key, fragment.mIndex);
+ }
+
+ public Fragment getFragment(Bundle bundle, String key) {
+ int index = bundle.getInt(key);
+ if (index >= mActive.size()) {
+ throw new IllegalStateException("Fragement no longer exists for key "
+ + key + ": index " + index);
+ }
+ Fragment f = mActive.get(index);
+ if (f == null) {
+ throw new IllegalStateException("Fragement no longer exists for key "
+ + key + ": index " + index);
+ }
+ return f;
+ }
+
Animatable loadAnimatable(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animatable animObj = fragment.onCreateAnimatable(transit, enter,
@@ -387,6 +522,7 @@ public class FragmentManager {
return;
}
+ if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex);
mActive.set(f.mIndex, null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList