Fragment.java revision 5ae74d6e89a30e79ea85c487b32223ef55314985
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.app; 18 19import android.content.ComponentCallbacks; 20import android.content.Intent; 21import android.content.res.Configuration; 22import android.os.Bundle; 23import android.os.Parcel; 24import android.os.Parcelable; 25import android.util.AttributeSet; 26import android.util.SparseArray; 27import android.view.LayoutInflater; 28import android.view.View; 29import android.view.ViewGroup; 30import android.view.animation.Animation; 31 32import java.lang.reflect.InvocationTargetException; 33import java.util.HashMap; 34 35final class FragmentState implements Parcelable { 36 static final String VIEW_STATE_TAG = "android:view_state"; 37 38 final String mClassName; 39 final int mIndex; 40 final boolean mFromLayout; 41 final int mFragmentId; 42 final int mContainerId; 43 final String mTag; 44 final boolean mRetainInstance; 45 46 Bundle mSavedFragmentState; 47 48 Fragment mInstance; 49 50 public FragmentState(Fragment frag) { 51 mClassName = frag.getClass().getName(); 52 mIndex = frag.mIndex; 53 mFromLayout = frag.mFromLayout; 54 mFragmentId = frag.mFragmentId; 55 mContainerId = frag.mContainerId; 56 mTag = frag.mTag; 57 mRetainInstance = frag.mRetainInstance; 58 } 59 60 public FragmentState(Parcel in) { 61 mClassName = in.readString(); 62 mIndex = in.readInt(); 63 mFromLayout = in.readInt() != 0; 64 mFragmentId = in.readInt(); 65 mContainerId = in.readInt(); 66 mTag = in.readString(); 67 mRetainInstance = in.readInt() != 0; 68 mSavedFragmentState = in.readBundle(); 69 } 70 71 public Fragment instantiate(Activity activity) { 72 if (mInstance != null) { 73 return mInstance; 74 } 75 76 try { 77 mInstance = Fragment.instantiate(activity, mClassName); 78 } catch (Exception e) { 79 throw new RuntimeException("Unable to restore fragment " + mClassName, e); 80 } 81 82 if (mSavedFragmentState != null) { 83 mSavedFragmentState.setClassLoader(activity.getClassLoader()); 84 mInstance.mSavedFragmentState = mSavedFragmentState; 85 mInstance.mSavedViewState 86 = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG); 87 } 88 mInstance.setIndex(mIndex); 89 mInstance.mFromLayout = mFromLayout; 90 mInstance.mFragmentId = mFragmentId; 91 mInstance.mContainerId = mContainerId; 92 mInstance.mTag = mTag; 93 mInstance.mRetainInstance = mRetainInstance; 94 95 return mInstance; 96 } 97 98 public int describeContents() { 99 return 0; 100 } 101 102 public void writeToParcel(Parcel dest, int flags) { 103 dest.writeString(mClassName); 104 dest.writeInt(mIndex); 105 dest.writeInt(mFromLayout ? 1 : 0); 106 dest.writeInt(mFragmentId); 107 dest.writeInt(mContainerId); 108 dest.writeString(mTag); 109 dest.writeInt(mRetainInstance ? 1 : 0); 110 dest.writeBundle(mSavedFragmentState); 111 } 112 113 public static final Parcelable.Creator<FragmentState> CREATOR 114 = new Parcelable.Creator<FragmentState>() { 115 public FragmentState createFromParcel(Parcel in) { 116 return new FragmentState(in); 117 } 118 119 public FragmentState[] newArray(int size) { 120 return new FragmentState[size]; 121 } 122 }; 123} 124 125/** 126 * A Fragment is a piece of an application's user interface or behavior 127 * that can be placed in an {@link Activity}. 128 */ 129public class Fragment implements ComponentCallbacks { 130 private static final HashMap<String, Class> sClassMap = 131 new HashMap<String, Class>(); 132 133 static final int INITIALIZING = 0; // Not yet created. 134 static final int CREATED = 1; // Created. 135 static final int CONTENT = 2; // View hierarchy content available. 136 static final int STARTED = 3; // Created and started, not resumed. 137 static final int RESUMED = 4; // Created started and resumed. 138 139 int mState = INITIALIZING; 140 141 // When instantiated from saved state, this is the saved state. 142 Bundle mSavedFragmentState; 143 SparseArray<Parcelable> mSavedViewState; 144 145 // Index into active fragment array. 146 int mIndex = -1; 147 148 // Internal unique name for this fragment; 149 String mWho; 150 151 // True if the fragment is in the list of added fragments. 152 boolean mAdded; 153 154 // Set to true if this fragment was instantiated from a layout file. 155 boolean mFromLayout; 156 157 // Number of active back stack entries this fragment is in. 158 int mBackStackNesting; 159 160 // Activity this fragment is attached to. 161 Activity mActivity; 162 163 // The optional identifier for this fragment -- either the container ID if it 164 // was dynamically added to the view hierarchy, or the ID supplied in 165 // layout. 166 int mFragmentId; 167 168 // When a fragment is being dynamically added to the view hierarchy, this 169 // is the identifier of the parent container it is being added to. 170 int mContainerId; 171 172 // The optional named tag for this fragment -- usually used to find 173 // fragments that are not part of the layout. 174 String mTag; 175 176 // Set to true when the app has requested that this fragment be hidden 177 // from the user. 178 boolean mHidden; 179 180 // If set this fragment would like its instance retained across 181 // configuration changes. 182 boolean mRetainInstance; 183 184 // If set this fragment is being retained across the current config change. 185 boolean mRetaining; 186 187 // Used to verify that subclasses call through to super class. 188 boolean mCalled; 189 190 // If app has requested a specific animation, this is the one to use. 191 int mNextAnim; 192 193 // The parent container of the fragment after dynamically added to UI. 194 ViewGroup mContainer; 195 196 // The View generated for this fragment. 197 View mView; 198 199 public Fragment() { 200 } 201 202 static Fragment instantiate(Activity activity, String fname) 203 throws NoSuchMethodException, ClassNotFoundException, 204 IllegalArgumentException, InstantiationException, 205 IllegalAccessException, InvocationTargetException { 206 Class clazz = sClassMap.get(fname); 207 208 if (clazz == null) { 209 // Class not found in the cache, see if it's real, and try to add it 210 clazz = activity.getClassLoader().loadClass(fname); 211 sClassMap.put(fname, clazz); 212 } 213 return (Fragment)clazz.newInstance(); 214 } 215 216 void restoreViewState() { 217 if (mSavedViewState != null) { 218 mView.restoreHierarchyState(mSavedViewState); 219 mSavedViewState = null; 220 } 221 } 222 223 void setIndex(int index) { 224 mIndex = index; 225 mWho = "android:fragment:" + mIndex; 226 } 227 228 void clearIndex() { 229 mIndex = -1; 230 mWho = null; 231 } 232 233 /** 234 * Subclasses can not override equals(). 235 */ 236 @Override final public boolean equals(Object o) { 237 return super.equals(o); 238 } 239 240 /** 241 * Subclasses can not override hashCode(). 242 */ 243 @Override final public int hashCode() { 244 return super.hashCode(); 245 } 246 247 @Override 248 public String toString() { 249 StringBuilder sb = new StringBuilder(128); 250 sb.append("Fragment{"); 251 sb.append(Integer.toHexString(System.identityHashCode(this))); 252 if (mIndex >= 0) { 253 sb.append(" #"); 254 sb.append(mIndex); 255 } 256 if (mFragmentId != 0) { 257 sb.append(" id=0x"); 258 sb.append(Integer.toHexString(mFragmentId)); 259 } 260 if (mTag != null) { 261 sb.append(" "); 262 sb.append(mTag); 263 } 264 sb.append('}'); 265 return sb.toString(); 266 } 267 268 /** 269 * Return the identifier this fragment is known by. This is either 270 * the android:id value supplied in a layout or the container view ID 271 * supplied when adding the fragment. 272 */ 273 public int getId() { 274 return mFragmentId; 275 } 276 277 /** 278 * Get the tag name of the fragment, if specified. 279 */ 280 public String getTag() { 281 return mTag; 282 } 283 284 /** 285 * Return the Activity this fragment is currently associated with. 286 */ 287 public Activity getActivity() { 288 return mActivity; 289 } 290 291 /** 292 * Return true if the fragment is currently added to its activity. 293 */ 294 public boolean isAdded() { 295 return mActivity != null && mActivity.mFragments.mAdded.contains(this); 296 } 297 298 /** 299 * Return true if the fragment is currently visible to the user. This means 300 * it: (1) has been added, (2) has its view attached to the window, and 301 * (3) is not hidden. 302 */ 303 public boolean isVisible() { 304 return isAdded() && !isHidden() && mView != null 305 && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE; 306 } 307 308 /** 309 * Return true if the fragment has been hidden. By default fragments 310 * are shown. You can find out about changes to this state with 311 * {@link #onHiddenChanged()}. Note that the hidden state is orthogonal 312 * to other states -- that is, to be visible to the user, a fragment 313 * must be both started and not hidden. 314 */ 315 public boolean isHidden() { 316 return mHidden; 317 } 318 319 /** 320 * Called when the hidden state (as returned by {@link #isHidden()} of 321 * the fragment has changed. Fragments start out not hidden; this will 322 * be called whenever the fragment changes state from that. 323 * @param hidden True if the fragment is now hidden, false if it is not 324 * visible. 325 */ 326 public void onHiddenChanged(boolean hidden) { 327 } 328 329 /** 330 * Control whether a fragment instance is retained across Activity 331 * re-creation (such as from a configuration change). This can only 332 * be used with fragments not in the back stack. If set, the fragment 333 * lifecycle will be slightly different when an activity is recreated: 334 * <ul> 335 * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still 336 * will be, because the fragment is being detached from its current activity). 337 * <li> {@link #onCreate(Bundle)} will not be called since the fragment 338 * is not being re-created. 339 * <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b> 340 * still be called. 341 * </ul> 342 */ 343 public void setRetainInstance(boolean retain) { 344 mRetainInstance = retain; 345 } 346 347 public boolean getRetainInstance() { 348 return mRetainInstance; 349 } 350 351 /** 352 * Call {@link Activity#startActivity(Intent)} on the fragment's 353 * containing Activity. 354 */ 355 public void startActivity(Intent intent) { 356 mActivity.startActivityFromFragment(this, intent, -1); 357 } 358 359 /** 360 * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's 361 * containing Activity. 362 */ 363 public void startActivityForResult(Intent intent, int requestCode) { 364 mActivity.startActivityFromFragment(this, intent, requestCode); 365 } 366 367 /** 368 * Receive the result from a previous call to 369 * {@link #startActivityForResult(Intent, int)}. This follows the 370 * related Activity API as described there in 371 * {@link Activity#onActivityResult(int, int, Intent)}. 372 * 373 * @param requestCode The integer request code originally supplied to 374 * startActivityForResult(), allowing you to identify who this 375 * result came from. 376 * @param resultCode The integer result code returned by the child activity 377 * through its setResult(). 378 * @param data An Intent, which can return result data to the caller 379 * (various data can be attached to Intent "extras"). 380 */ 381 public void onActivityResult(int requestCode, int resultCode, Intent data) { 382 } 383 384 /** 385 * Called when a fragment is being created as part of a view layout 386 * inflation, typically from setting the content view of an activity. This 387 * will be called both the first time the fragment is created, as well 388 * later when it is being re-created from its saved state (which is also 389 * given here). 390 * 391 * XXX This is kind-of yucky... maybe we could just supply the 392 * AttributeSet to onCreate()? 393 * 394 * @param activity The Activity that is inflating the fragment. 395 * @param attrs The attributes at the tag where the fragment is 396 * being created. 397 * @param savedInstanceState If the fragment is being re-created from 398 * a previous saved state, this is the state. 399 */ 400 public void onInflate(Activity activity, AttributeSet attrs, 401 Bundle savedInstanceState) { 402 mCalled = true; 403 } 404 405 /** 406 * Called when a fragment is first attached to its activity. 407 * {@link #onCreate(Bundle)} will be called after this. 408 */ 409 public void onAttach(Activity activity) { 410 mCalled = true; 411 } 412 413 public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 414 return null; 415 } 416 417 /** 418 * Called to do initial creation of a fragment. This is called after 419 * {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}. 420 * @param savedInstanceState If the fragment is being re-created from 421 * a previous saved state, this is the state. 422 */ 423 public void onCreate(Bundle savedInstanceState) { 424 mCalled = true; 425 } 426 427 /** 428 * Called to have the fragment instantiate its user interface view. 429 * This is optional, and non-graphical fragments can return null (which 430 * is the default implementation). This will be called between 431 * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}. 432 * 433 * @param inflater The LayoutInflater object that can be used to inflate 434 * any views in the fragment, 435 * @param container If non-null, this is the parent view that the fragment's 436 * UI should be attached to. The fragment should not add the view itself, 437 * but this can be used to generate the LayoutParams of the view. 438 * @param savedInstanceState If non-null, this fragment is being re-constructed 439 * from a previous saved state as given here. 440 * 441 * @return Return the View for the fragment's UI, or null. 442 */ 443 public View onCreateView(LayoutInflater inflater, ViewGroup container, 444 Bundle savedInstanceState) { 445 return null; 446 } 447 448 public View getView() { 449 return mView; 450 } 451 452 /** 453 * Called when the activity is ready for the fragment to run. This is 454 * most useful for fragments that use {@link #setRetainInstance(boolean)} 455 * instance, as this tells the fragment when it is fully associated with 456 * the new activity instance. This is called after {@link #onCreate(Bundle)} 457 * and before {@link #onStart()}. 458 * 459 * @param savedInstanceState If the fragment is being re-created from 460 * a previous saved state, this is the state. 461 */ 462 public void onReady(Bundle savedInstanceState) { 463 mCalled = true; 464 } 465 466 /** 467 * Called when the Fragment is visible to the user. This is generally 468 * tied to {@link Activity#onStart() Activity.onStart} of the containing 469 * Activity's lifecycle. 470 */ 471 public void onStart() { 472 mCalled = true; 473 } 474 475 /** 476 * Called when the fragment is visible to the user and actively running. 477 * This is generally 478 * tied to {@link Activity#onResume() Activity.onResume} of the containing 479 * Activity's lifecycle. 480 */ 481 public void onResume() { 482 mCalled = true; 483 } 484 485 public void onSaveInstanceState(Bundle outState) { 486 } 487 488 public void onConfigurationChanged(Configuration newConfig) { 489 mCalled = true; 490 } 491 492 /** 493 * Called when the Fragment is no longer resumed. This is generally 494 * tied to {@link Activity#onPause() Activity.onPause} of the containing 495 * Activity's lifecycle. 496 */ 497 public void onPause() { 498 mCalled = true; 499 } 500 501 /** 502 * Called when the Fragment is no longer started. This is generally 503 * tied to {@link Activity#onStop() Activity.onStop} of the containing 504 * Activity's lifecycle. 505 */ 506 public void onStop() { 507 mCalled = true; 508 } 509 510 public void onLowMemory() { 511 mCalled = true; 512 } 513 514 /** 515 * Called when the fragment is no longer in use. This is called 516 * after {@link #onStop()} and before {@link #onDetach()}. 517 */ 518 public void onDestroy() { 519 mCalled = true; 520 } 521 522 /** 523 * Called when the fragment is no longer attached to its activity. This 524 * is called after {@link #onDestroy()}. 525 */ 526 public void onDetach() { 527 mCalled = true; 528 } 529} 530