Fragment.java revision c801768e4d29667a2608695449ebc2833ba0f200
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.ContextMenu; 28import android.view.LayoutInflater; 29import android.view.Menu; 30import android.view.MenuInflater; 31import android.view.MenuItem; 32import android.view.View; 33import android.view.ViewGroup; 34import android.view.ContextMenu.ContextMenuInfo; 35import android.view.View.OnCreateContextMenuListener; 36import android.view.animation.Animation; 37import android.widget.AdapterView; 38 39import java.lang.reflect.InvocationTargetException; 40import java.util.HashMap; 41 42final class FragmentState implements Parcelable { 43 static final String VIEW_STATE_TAG = "android:view_state"; 44 45 final String mClassName; 46 final int mIndex; 47 final boolean mFromLayout; 48 final int mFragmentId; 49 final int mContainerId; 50 final String mTag; 51 final boolean mRetainInstance; 52 53 Bundle mSavedFragmentState; 54 55 Fragment mInstance; 56 57 public FragmentState(Fragment frag) { 58 mClassName = frag.getClass().getName(); 59 mIndex = frag.mIndex; 60 mFromLayout = frag.mFromLayout; 61 mFragmentId = frag.mFragmentId; 62 mContainerId = frag.mContainerId; 63 mTag = frag.mTag; 64 mRetainInstance = frag.mRetainInstance; 65 } 66 67 public FragmentState(Parcel in) { 68 mClassName = in.readString(); 69 mIndex = in.readInt(); 70 mFromLayout = in.readInt() != 0; 71 mFragmentId = in.readInt(); 72 mContainerId = in.readInt(); 73 mTag = in.readString(); 74 mRetainInstance = in.readInt() != 0; 75 mSavedFragmentState = in.readBundle(); 76 } 77 78 public Fragment instantiate(Activity activity) { 79 if (mInstance != null) { 80 return mInstance; 81 } 82 83 try { 84 mInstance = Fragment.instantiate(activity, mClassName); 85 } catch (Exception e) { 86 throw new RuntimeException("Unable to restore fragment " + mClassName, e); 87 } 88 89 if (mSavedFragmentState != null) { 90 mSavedFragmentState.setClassLoader(activity.getClassLoader()); 91 mInstance.mSavedFragmentState = mSavedFragmentState; 92 mInstance.mSavedViewState 93 = mSavedFragmentState.getSparseParcelableArray(VIEW_STATE_TAG); 94 } 95 mInstance.setIndex(mIndex); 96 mInstance.mFromLayout = mFromLayout; 97 mInstance.mFragmentId = mFragmentId; 98 mInstance.mContainerId = mContainerId; 99 mInstance.mTag = mTag; 100 mInstance.mRetainInstance = mRetainInstance; 101 102 return mInstance; 103 } 104 105 public int describeContents() { 106 return 0; 107 } 108 109 public void writeToParcel(Parcel dest, int flags) { 110 dest.writeString(mClassName); 111 dest.writeInt(mIndex); 112 dest.writeInt(mFromLayout ? 1 : 0); 113 dest.writeInt(mFragmentId); 114 dest.writeInt(mContainerId); 115 dest.writeString(mTag); 116 dest.writeInt(mRetainInstance ? 1 : 0); 117 dest.writeBundle(mSavedFragmentState); 118 } 119 120 public static final Parcelable.Creator<FragmentState> CREATOR 121 = new Parcelable.Creator<FragmentState>() { 122 public FragmentState createFromParcel(Parcel in) { 123 return new FragmentState(in); 124 } 125 126 public FragmentState[] newArray(int size) { 127 return new FragmentState[size]; 128 } 129 }; 130} 131 132/** 133 * A Fragment is a piece of an application's user interface or behavior 134 * that can be placed in an {@link Activity}. 135 */ 136public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener { 137 private static final HashMap<String, Class<?>> sClassMap = 138 new HashMap<String, Class<?>>(); 139 140 static final int INITIALIZING = 0; // Not yet created. 141 static final int CREATED = 1; // Created. 142 static final int ACTIVITY_CREATED = 2; // The activity has finished its creation. 143 static final int STARTED = 3; // Created and started, not resumed. 144 static final int RESUMED = 4; // Created started and resumed. 145 146 int mState = INITIALIZING; 147 148 // When instantiated from saved state, this is the saved state. 149 Bundle mSavedFragmentState; 150 SparseArray<Parcelable> mSavedViewState; 151 152 // Index into active fragment array. 153 int mIndex = -1; 154 155 // Internal unique name for this fragment; 156 String mWho; 157 158 // True if the fragment is in the list of added fragments. 159 boolean mAdded; 160 161 // Set to true if this fragment was instantiated from a layout file. 162 boolean mFromLayout; 163 164 // Number of active back stack entries this fragment is in. 165 int mBackStackNesting; 166 167 // Set as soon as a fragment is added to a transaction (or removed), 168 // to be able to do validation. 169 Activity mImmediateActivity; 170 171 // Activity this fragment is attached to. 172 Activity mActivity; 173 174 // The optional identifier for this fragment -- either the container ID if it 175 // was dynamically added to the view hierarchy, or the ID supplied in 176 // layout. 177 int mFragmentId; 178 179 // When a fragment is being dynamically added to the view hierarchy, this 180 // is the identifier of the parent container it is being added to. 181 int mContainerId; 182 183 // The optional named tag for this fragment -- usually used to find 184 // fragments that are not part of the layout. 185 String mTag; 186 187 // Set to true when the app has requested that this fragment be hidden 188 // from the user. 189 boolean mHidden; 190 191 // If set this fragment would like its instance retained across 192 // configuration changes. 193 boolean mRetainInstance; 194 195 // If set this fragment is being retained across the current config change. 196 boolean mRetaining; 197 198 // If set this fragment has menu items to contribute. 199 boolean mHasMenu; 200 201 // Used to verify that subclasses call through to super class. 202 boolean mCalled; 203 204 // If app has requested a specific animation, this is the one to use. 205 int mNextAnim; 206 207 // The parent container of the fragment after dynamically added to UI. 208 ViewGroup mContainer; 209 210 // The View generated for this fragment. 211 View mView; 212 213 LoaderManager mLoaderManager; 214 boolean mStarted; 215 216 /** 217 * Default constructor. <strong>Every</string> fragment must have an 218 * empty constructor, so it can be instantiated when restoring its 219 * activity's state. It is strongly recommended that subclasses do not 220 * have other constructors with parameters, since these constructors 221 * will not be called when the fragment is re-instantiated; instead, 222 * retrieve such parameters from the activity in {@link #onAttach(Activity)}. 223 */ 224 public Fragment() { 225 } 226 227 static Fragment instantiate(Activity activity, String fname) 228 throws NoSuchMethodException, ClassNotFoundException, 229 IllegalArgumentException, InstantiationException, 230 IllegalAccessException, InvocationTargetException { 231 Class<?> clazz = sClassMap.get(fname); 232 233 if (clazz == null) { 234 // Class not found in the cache, see if it's real, and try to add it 235 clazz = activity.getClassLoader().loadClass(fname); 236 sClassMap.put(fname, clazz); 237 } 238 return (Fragment)clazz.newInstance(); 239 } 240 241 void restoreViewState() { 242 if (mSavedViewState != null) { 243 mView.restoreHierarchyState(mSavedViewState); 244 mSavedViewState = null; 245 } 246 } 247 248 void setIndex(int index) { 249 mIndex = index; 250 mWho = "android:fragment:" + mIndex; 251 } 252 253 void clearIndex() { 254 mIndex = -1; 255 mWho = null; 256 } 257 258 /** 259 * Subclasses can not override equals(). 260 */ 261 @Override final public boolean equals(Object o) { 262 return super.equals(o); 263 } 264 265 /** 266 * Subclasses can not override hashCode(). 267 */ 268 @Override final public int hashCode() { 269 return super.hashCode(); 270 } 271 272 @Override 273 public String toString() { 274 StringBuilder sb = new StringBuilder(128); 275 sb.append("Fragment{"); 276 sb.append(Integer.toHexString(System.identityHashCode(this))); 277 if (mIndex >= 0) { 278 sb.append(" #"); 279 sb.append(mIndex); 280 } 281 if (mFragmentId != 0) { 282 sb.append(" id=0x"); 283 sb.append(Integer.toHexString(mFragmentId)); 284 } 285 if (mTag != null) { 286 sb.append(" "); 287 sb.append(mTag); 288 } 289 sb.append('}'); 290 return sb.toString(); 291 } 292 293 /** 294 * Return the identifier this fragment is known by. This is either 295 * the android:id value supplied in a layout or the container view ID 296 * supplied when adding the fragment. 297 */ 298 final public int getId() { 299 return mFragmentId; 300 } 301 302 /** 303 * Get the tag name of the fragment, if specified. 304 */ 305 final public String getTag() { 306 return mTag; 307 } 308 309 /** 310 * Return the Activity this fragment is currently associated with. 311 */ 312 final public Activity getActivity() { 313 return mActivity; 314 } 315 316 /** 317 * Return true if the fragment is currently added to its activity. 318 */ 319 final public boolean isAdded() { 320 return mActivity != null && mActivity.mFragments.mAdded.contains(this); 321 } 322 323 /** 324 * Return true if the fragment is currently visible to the user. This means 325 * it: (1) has been added, (2) has its view attached to the window, and 326 * (3) is not hidden. 327 */ 328 final public boolean isVisible() { 329 return isAdded() && !isHidden() && mView != null 330 && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE; 331 } 332 333 /** 334 * Return true if the fragment has been hidden. By default fragments 335 * are shown. You can find out about changes to this state with 336 * {@link #onHiddenChanged}. Note that the hidden state is orthogonal 337 * to other states -- that is, to be visible to the user, a fragment 338 * must be both started and not hidden. 339 */ 340 final public boolean isHidden() { 341 return mHidden; 342 } 343 344 /** 345 * Called when the hidden state (as returned by {@link #isHidden()} of 346 * the fragment has changed. Fragments start out not hidden; this will 347 * be called whenever the fragment changes state from that. 348 * @param hidden True if the fragment is now hidden, false if it is not 349 * visible. 350 */ 351 public void onHiddenChanged(boolean hidden) { 352 } 353 354 /** 355 * Control whether a fragment instance is retained across Activity 356 * re-creation (such as from a configuration change). This can only 357 * be used with fragments not in the back stack. If set, the fragment 358 * lifecycle will be slightly different when an activity is recreated: 359 * <ul> 360 * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still 361 * will be, because the fragment is being detached from its current activity). 362 * <li> {@link #onCreate(Bundle)} will not be called since the fragment 363 * is not being re-created. 364 * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b> 365 * still be called. 366 * </ul> 367 */ 368 public void setRetainInstance(boolean retain) { 369 mRetainInstance = retain; 370 } 371 372 final public boolean getRetainInstance() { 373 return mRetainInstance; 374 } 375 376 /** 377 * Report that this fragment would like to participate in populating 378 * the options menu by receiving a call to {@link #onCreateOptionsMenu} 379 * and related methods. 380 * 381 * @param hasMenu If true, the fragment has menu items to contribute. 382 */ 383 public void setHasOptionsMenu(boolean hasMenu) { 384 if (mHasMenu != hasMenu) { 385 mHasMenu = hasMenu; 386 if (isAdded() && !isHidden()) { 387 mActivity.invalidateOptionsMenu(); 388 } 389 } 390 } 391 392 /** 393 * Return the LoaderManager for this fragment, creating it if needed. 394 */ 395 public LoaderManager getLoaderManager() { 396 if (mLoaderManager != null) { 397 return mLoaderManager; 398 } 399 mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted); 400 return mLoaderManager; 401 } 402 403 /** 404 * Call {@link Activity#startActivity(Intent)} on the fragment's 405 * containing Activity. 406 */ 407 public void startActivity(Intent intent) { 408 mActivity.startActivityFromFragment(this, intent, -1); 409 } 410 411 /** 412 * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's 413 * containing Activity. 414 */ 415 public void startActivityForResult(Intent intent, int requestCode) { 416 mActivity.startActivityFromFragment(this, intent, requestCode); 417 } 418 419 /** 420 * Receive the result from a previous call to 421 * {@link #startActivityForResult(Intent, int)}. This follows the 422 * related Activity API as described there in 423 * {@link Activity#onActivityResult(int, int, Intent)}. 424 * 425 * @param requestCode The integer request code originally supplied to 426 * startActivityForResult(), allowing you to identify who this 427 * result came from. 428 * @param resultCode The integer result code returned by the child activity 429 * through its setResult(). 430 * @param data An Intent, which can return result data to the caller 431 * (various data can be attached to Intent "extras"). 432 */ 433 public void onActivityResult(int requestCode, int resultCode, Intent data) { 434 } 435 436 /** 437 * Called when a fragment is being created as part of a view layout 438 * inflation, typically from setting the content view of an activity. This 439 * will be called both the first time the fragment is created, as well 440 * later when it is being re-created from its saved state (which is also 441 * given here). 442 * 443 * XXX This is kind-of yucky... maybe we could just supply the 444 * AttributeSet to onCreate()? 445 * 446 * @param activity The Activity that is inflating the fragment. 447 * @param attrs The attributes at the tag where the fragment is 448 * being created. 449 * @param savedInstanceState If the fragment is being re-created from 450 * a previous saved state, this is the state. 451 */ 452 public void onInflate(Activity activity, AttributeSet attrs, 453 Bundle savedInstanceState) { 454 mCalled = true; 455 } 456 457 /** 458 * Called when a fragment is first attached to its activity. 459 * {@link #onCreate(Bundle)} will be called after this. 460 */ 461 public void onAttach(Activity activity) { 462 mCalled = true; 463 } 464 465 public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 466 return null; 467 } 468 469 /** 470 * Called to do initial creation of a fragment. This is called after 471 * {@link #onAttach(Activity)} and before 472 * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. 473 * 474 * <p>Note that this can be called while the fragment's activity is 475 * still in the process of being created. As such, you can not rely 476 * on things like the activity's content view hierarchy being initialized 477 * at this point. If you want to do work once the activity itself is 478 * created, see {@link #onActivityCreated(Bundle)}. 479 * 480 * @param savedInstanceState If the fragment is being re-created from 481 * a previous saved state, this is the state. 482 */ 483 public void onCreate(Bundle savedInstanceState) { 484 mCalled = true; 485 } 486 487 /** 488 * Called to have the fragment instantiate its user interface view. 489 * This is optional, and non-graphical fragments can return null (which 490 * is the default implementation). This will be called between 491 * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}. 492 * 493 * <p>If you return a View from here, you will later be called in 494 * {@link #onDestroyView} when the view is being released. 495 * 496 * @param inflater The LayoutInflater object that can be used to inflate 497 * any views in the fragment, 498 * @param container If non-null, this is the parent view that the fragment's 499 * UI should be attached to. The fragment should not add the view itself, 500 * but this can be used to generate the LayoutParams of the view. 501 * @param savedInstanceState If non-null, this fragment is being re-constructed 502 * from a previous saved state as given here. 503 * 504 * @return Return the View for the fragment's UI, or null. 505 */ 506 public View onCreateView(LayoutInflater inflater, ViewGroup container, 507 Bundle savedInstanceState) { 508 return null; 509 } 510 511 public View getView() { 512 return mView; 513 } 514 515 /** 516 * Called when the fragment's activity has been created and this 517 * fragment's view hierarchy instantiated. It can be used to do final 518 * initialization once these pieces are in place, such as retrieving 519 * views or restoring state. It is also useful for fragments that use 520 * {@link #setRetainInstance(boolean)} to retain their instance, 521 * as this callback tells the fragment when it is fully associated with 522 * the new activity instance. This is called after {@link #onCreateView} 523 * and before {@link #onStart()}. 524 * 525 * @param savedInstanceState If the fragment is being re-created from 526 * a previous saved state, this is the state. 527 */ 528 public void onActivityCreated(Bundle savedInstanceState) { 529 mCalled = true; 530 } 531 532 /** 533 * Called when the Fragment is visible to the user. This is generally 534 * tied to {@link Activity#onStart() Activity.onStart} of the containing 535 * Activity's lifecycle. 536 */ 537 public void onStart() { 538 mCalled = true; 539 mStarted = true; 540 if (mLoaderManager != null) { 541 mLoaderManager.doStart(); 542 } 543 } 544 545 /** 546 * Called when the fragment is visible to the user and actively running. 547 * This is generally 548 * tied to {@link Activity#onResume() Activity.onResume} of the containing 549 * Activity's lifecycle. 550 */ 551 public void onResume() { 552 mCalled = true; 553 } 554 555 public void onSaveInstanceState(Bundle outState) { 556 } 557 558 public void onConfigurationChanged(Configuration newConfig) { 559 mCalled = true; 560 } 561 562 /** 563 * Called when the Fragment is no longer resumed. This is generally 564 * tied to {@link Activity#onPause() Activity.onPause} of the containing 565 * Activity's lifecycle. 566 */ 567 public void onPause() { 568 mCalled = true; 569 } 570 571 /** 572 * Called when the Fragment is no longer started. This is generally 573 * tied to {@link Activity#onStop() Activity.onStop} of the containing 574 * Activity's lifecycle. 575 */ 576 public void onStop() { 577 mCalled = true; 578 mStarted = false; 579 if (mLoaderManager != null) { 580 mLoaderManager.doStop(); 581 } 582 } 583 584 public void onLowMemory() { 585 mCalled = true; 586 } 587 588 /** 589 * Called when the view previously created by {@link #onCreateView} has 590 * been detached from the fragment. The next time the fragment needs 591 * to be displayed, a new view will be created. This is called 592 * after {@link #onStop()} and before {@link #onDestroy()}; it is only 593 * called if {@link #onCreateView} returns a non-null View. 594 */ 595 public void onDestroyView() { 596 mCalled = true; 597 } 598 599 /** 600 * Called when the fragment is no longer in use. This is called 601 * after {@link #onStop()} and before {@link #onDetach()}. 602 */ 603 public void onDestroy() { 604 mCalled = true; 605 if (mLoaderManager != null) { 606 mLoaderManager.doDestroy(); 607 } 608 } 609 610 /** 611 * Called when the fragment is no longer attached to its activity. This 612 * is called after {@link #onDestroy()}. 613 */ 614 public void onDetach() { 615 mCalled = true; 616 } 617 618 /** 619 * Initialize the contents of the Activity's standard options menu. You 620 * should place your menu items in to <var>menu</var>. For this method 621 * to be called, you must have first called {@link #setHasOptionsMenu}. See 622 * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu} 623 * for more information. 624 * 625 * @param menu The options menu in which you place your items. 626 * 627 * @see #setHasOptionsMenu 628 * @see #onPrepareOptionsMenu 629 * @see #onOptionsItemSelected 630 */ 631 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 632 } 633 634 /** 635 * Prepare the Screen's standard options menu to be displayed. This is 636 * called right before the menu is shown, every time it is shown. You can 637 * use this method to efficiently enable/disable items or otherwise 638 * dynamically modify the contents. See 639 * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu} 640 * for more information. 641 * 642 * @param menu The options menu as last shown or first initialized by 643 * onCreateOptionsMenu(). 644 * 645 * @see #setHasOptionsMenu 646 * @see #onCreateOptionsMenu 647 */ 648 public void onPrepareOptionsMenu(Menu menu) { 649 } 650 651 /** 652 * This hook is called whenever an item in your options menu is selected. 653 * The default implementation simply returns false to have the normal 654 * processing happen (calling the item's Runnable or sending a message to 655 * its Handler as appropriate). You can use this method for any items 656 * for which you would like to do processing without those other 657 * facilities. 658 * 659 * <p>Derived classes should call through to the base class for it to 660 * perform the default menu handling. 661 * 662 * @param item The menu item that was selected. 663 * 664 * @return boolean Return false to allow normal menu processing to 665 * proceed, true to consume it here. 666 * 667 * @see #onCreateOptionsMenu 668 */ 669 public boolean onOptionsItemSelected(MenuItem item) { 670 return false; 671 } 672 673 /** 674 * This hook is called whenever the options menu is being closed (either by the user canceling 675 * the menu with the back/menu button, or when an item is selected). 676 * 677 * @param menu The options menu as last shown or first initialized by 678 * onCreateOptionsMenu(). 679 */ 680 public void onOptionsMenuClosed(Menu menu) { 681 } 682 683 /** 684 * Called when a context menu for the {@code view} is about to be shown. 685 * Unlike {@link #onCreateOptionsMenu}, this will be called every 686 * time the context menu is about to be shown and should be populated for 687 * the view (or item inside the view for {@link AdapterView} subclasses, 688 * this can be found in the {@code menuInfo})). 689 * <p> 690 * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an 691 * item has been selected. 692 * <p> 693 * The default implementation calls up to 694 * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though 695 * you can not call this implementation if you don't want that behavior. 696 * <p> 697 * It is not safe to hold onto the context menu after this method returns. 698 * {@inheritDoc} 699 */ 700 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 701 getActivity().onCreateContextMenu(menu, v, menuInfo); 702 } 703 704 /** 705 * Registers a context menu to be shown for the given view (multiple views 706 * can show the context menu). This method will set the 707 * {@link OnCreateContextMenuListener} on the view to this fragment, so 708 * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be 709 * called when it is time to show the context menu. 710 * 711 * @see #unregisterForContextMenu(View) 712 * @param view The view that should show a context menu. 713 */ 714 public void registerForContextMenu(View view) { 715 view.setOnCreateContextMenuListener(this); 716 } 717 718 /** 719 * Prevents a context menu to be shown for the given view. This method will 720 * remove the {@link OnCreateContextMenuListener} on the view. 721 * 722 * @see #registerForContextMenu(View) 723 * @param view The view that should stop showing a context menu. 724 */ 725 public void unregisterForContextMenu(View view) { 726 view.setOnCreateContextMenuListener(null); 727 } 728 729 /** 730 * This hook is called whenever an item in a context menu is selected. The 731 * default implementation simply returns false to have the normal processing 732 * happen (calling the item's Runnable or sending a message to its Handler 733 * as appropriate). You can use this method for any items for which you 734 * would like to do processing without those other facilities. 735 * <p> 736 * Use {@link MenuItem#getMenuInfo()} to get extra information set by the 737 * View that added this menu item. 738 * <p> 739 * Derived classes should call through to the base class for it to perform 740 * the default menu handling. 741 * 742 * @param item The context menu item that was selected. 743 * @return boolean Return false to allow normal context menu processing to 744 * proceed, true to consume it here. 745 */ 746 public boolean onContextItemSelected(MenuItem item) { 747 return false; 748 } 749} 750