Fragment.java revision 5ddd127d5a38d80c0d8087d1770f41f61f84f048
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 CONTENT = 2; // View hierarchy content available. 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 public Fragment() { 214 } 215 216 static Fragment instantiate(Activity activity, String fname) 217 throws NoSuchMethodException, ClassNotFoundException, 218 IllegalArgumentException, InstantiationException, 219 IllegalAccessException, InvocationTargetException { 220 Class clazz = sClassMap.get(fname); 221 222 if (clazz == null) { 223 // Class not found in the cache, see if it's real, and try to add it 224 clazz = activity.getClassLoader().loadClass(fname); 225 sClassMap.put(fname, clazz); 226 } 227 return (Fragment)clazz.newInstance(); 228 } 229 230 void restoreViewState() { 231 if (mSavedViewState != null) { 232 mView.restoreHierarchyState(mSavedViewState); 233 mSavedViewState = null; 234 } 235 } 236 237 void setIndex(int index) { 238 mIndex = index; 239 mWho = "android:fragment:" + mIndex; 240 } 241 242 void clearIndex() { 243 mIndex = -1; 244 mWho = null; 245 } 246 247 /** 248 * Subclasses can not override equals(). 249 */ 250 @Override final public boolean equals(Object o) { 251 return super.equals(o); 252 } 253 254 /** 255 * Subclasses can not override hashCode(). 256 */ 257 @Override final public int hashCode() { 258 return super.hashCode(); 259 } 260 261 @Override 262 public String toString() { 263 StringBuilder sb = new StringBuilder(128); 264 sb.append("Fragment{"); 265 sb.append(Integer.toHexString(System.identityHashCode(this))); 266 if (mIndex >= 0) { 267 sb.append(" #"); 268 sb.append(mIndex); 269 } 270 if (mFragmentId != 0) { 271 sb.append(" id=0x"); 272 sb.append(Integer.toHexString(mFragmentId)); 273 } 274 if (mTag != null) { 275 sb.append(" "); 276 sb.append(mTag); 277 } 278 sb.append('}'); 279 return sb.toString(); 280 } 281 282 /** 283 * Return the identifier this fragment is known by. This is either 284 * the android:id value supplied in a layout or the container view ID 285 * supplied when adding the fragment. 286 */ 287 final public int getId() { 288 return mFragmentId; 289 } 290 291 /** 292 * Get the tag name of the fragment, if specified. 293 */ 294 final public String getTag() { 295 return mTag; 296 } 297 298 /** 299 * Return the Activity this fragment is currently associated with. 300 */ 301 final public Activity getActivity() { 302 return mActivity; 303 } 304 305 /** 306 * Return true if the fragment is currently added to its activity. 307 */ 308 final public boolean isAdded() { 309 return mActivity != null && mActivity.mFragments.mAdded.contains(this); 310 } 311 312 /** 313 * Return true if the fragment is currently visible to the user. This means 314 * it: (1) has been added, (2) has its view attached to the window, and 315 * (3) is not hidden. 316 */ 317 final public boolean isVisible() { 318 return isAdded() && !isHidden() && mView != null 319 && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE; 320 } 321 322 /** 323 * Return true if the fragment has been hidden. By default fragments 324 * are shown. You can find out about changes to this state with 325 * {@link #onHiddenChanged}. Note that the hidden state is orthogonal 326 * to other states -- that is, to be visible to the user, a fragment 327 * must be both started and not hidden. 328 */ 329 final public boolean isHidden() { 330 return mHidden; 331 } 332 333 /** 334 * Called when the hidden state (as returned by {@link #isHidden()} of 335 * the fragment has changed. Fragments start out not hidden; this will 336 * be called whenever the fragment changes state from that. 337 * @param hidden True if the fragment is now hidden, false if it is not 338 * visible. 339 */ 340 public void onHiddenChanged(boolean hidden) { 341 } 342 343 /** 344 * Control whether a fragment instance is retained across Activity 345 * re-creation (such as from a configuration change). This can only 346 * be used with fragments not in the back stack. If set, the fragment 347 * lifecycle will be slightly different when an activity is recreated: 348 * <ul> 349 * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still 350 * will be, because the fragment is being detached from its current activity). 351 * <li> {@link #onCreate(Bundle)} will not be called since the fragment 352 * is not being re-created. 353 * <li> {@link #onAttach(Activity)} and {@link #onReady(Bundle)} <b>will</b> 354 * still be called. 355 * </ul> 356 */ 357 public void setRetainInstance(boolean retain) { 358 mRetainInstance = retain; 359 } 360 361 final public boolean getRetainInstance() { 362 return mRetainInstance; 363 } 364 365 /** 366 * Report that this fragment would like to participate in populating 367 * the options menu by receiving a call to {@link #onCreateOptionsMenu} 368 * and related methods. 369 * 370 * @param hasMenu If true, the fragment has menu items to contribute. 371 */ 372 public void setHasOptionsMenu(boolean hasMenu) { 373 if (mHasMenu != hasMenu) { 374 mHasMenu = hasMenu; 375 if (isAdded() && !isHidden()) { 376 mActivity.invalidateOptionsMenu(); 377 } 378 } 379 } 380 381 /** 382 * Call {@link Activity#startActivity(Intent)} on the fragment's 383 * containing Activity. 384 */ 385 public void startActivity(Intent intent) { 386 mActivity.startActivityFromFragment(this, intent, -1); 387 } 388 389 /** 390 * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's 391 * containing Activity. 392 */ 393 public void startActivityForResult(Intent intent, int requestCode) { 394 mActivity.startActivityFromFragment(this, intent, requestCode); 395 } 396 397 /** 398 * Receive the result from a previous call to 399 * {@link #startActivityForResult(Intent, int)}. This follows the 400 * related Activity API as described there in 401 * {@link Activity#onActivityResult(int, int, Intent)}. 402 * 403 * @param requestCode The integer request code originally supplied to 404 * startActivityForResult(), allowing you to identify who this 405 * result came from. 406 * @param resultCode The integer result code returned by the child activity 407 * through its setResult(). 408 * @param data An Intent, which can return result data to the caller 409 * (various data can be attached to Intent "extras"). 410 */ 411 public void onActivityResult(int requestCode, int resultCode, Intent data) { 412 } 413 414 /** 415 * Called when a fragment is being created as part of a view layout 416 * inflation, typically from setting the content view of an activity. This 417 * will be called both the first time the fragment is created, as well 418 * later when it is being re-created from its saved state (which is also 419 * given here). 420 * 421 * XXX This is kind-of yucky... maybe we could just supply the 422 * AttributeSet to onCreate()? 423 * 424 * @param activity The Activity that is inflating the fragment. 425 * @param attrs The attributes at the tag where the fragment is 426 * being created. 427 * @param savedInstanceState If the fragment is being re-created from 428 * a previous saved state, this is the state. 429 */ 430 public void onInflate(Activity activity, AttributeSet attrs, 431 Bundle savedInstanceState) { 432 mCalled = true; 433 } 434 435 /** 436 * Called when a fragment is first attached to its activity. 437 * {@link #onCreate(Bundle)} will be called after this. 438 */ 439 public void onAttach(Activity activity) { 440 mCalled = true; 441 } 442 443 public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { 444 return null; 445 } 446 447 /** 448 * Called to do initial creation of a fragment. This is called after 449 * {@link #onAttach(Activity)} and before {@link #onReady(Bundle)}. 450 * @param savedInstanceState If the fragment is being re-created from 451 * a previous saved state, this is the state. 452 */ 453 public void onCreate(Bundle savedInstanceState) { 454 mCalled = true; 455 } 456 457 /** 458 * Called to have the fragment instantiate its user interface view. 459 * This is optional, and non-graphical fragments can return null (which 460 * is the default implementation). This will be called between 461 * {@link #onCreate(Bundle)} and {@link #onReady(Bundle)}. 462 * 463 * <p>If you return a View from here, you will later be called in 464 * {@link #onDestroyView} when the view is being released. 465 * 466 * @param inflater The LayoutInflater object that can be used to inflate 467 * any views in the fragment, 468 * @param container If non-null, this is the parent view that the fragment's 469 * UI should be attached to. The fragment should not add the view itself, 470 * but this can be used to generate the LayoutParams of the view. 471 * @param savedInstanceState If non-null, this fragment is being re-constructed 472 * from a previous saved state as given here. 473 * 474 * @return Return the View for the fragment's UI, or null. 475 */ 476 public View onCreateView(LayoutInflater inflater, ViewGroup container, 477 Bundle savedInstanceState) { 478 return null; 479 } 480 481 public View getView() { 482 return mView; 483 } 484 485 /** 486 * Called when the activity is ready for the fragment to run. This is 487 * most useful for fragments that use {@link #setRetainInstance(boolean)} 488 * instance, as this tells the fragment when it is fully associated with 489 * the new activity instance. This is called after {@link #onCreateView} 490 * and before {@link #onStart()}. 491 * 492 * @param savedInstanceState If the fragment is being re-created from 493 * a previous saved state, this is the state. 494 */ 495 public void onReady(Bundle savedInstanceState) { 496 mCalled = true; 497 } 498 499 /** 500 * Called when the Fragment is visible to the user. This is generally 501 * tied to {@link Activity#onStart() Activity.onStart} of the containing 502 * Activity's lifecycle. 503 */ 504 public void onStart() { 505 mCalled = true; 506 } 507 508 /** 509 * Called when the fragment is visible to the user and actively running. 510 * This is generally 511 * tied to {@link Activity#onResume() Activity.onResume} of the containing 512 * Activity's lifecycle. 513 */ 514 public void onResume() { 515 mCalled = true; 516 } 517 518 public void onSaveInstanceState(Bundle outState) { 519 } 520 521 public void onConfigurationChanged(Configuration newConfig) { 522 mCalled = true; 523 } 524 525 /** 526 * Called when the Fragment is no longer resumed. This is generally 527 * tied to {@link Activity#onPause() Activity.onPause} of the containing 528 * Activity's lifecycle. 529 */ 530 public void onPause() { 531 mCalled = true; 532 } 533 534 /** 535 * Called when the Fragment is no longer started. This is generally 536 * tied to {@link Activity#onStop() Activity.onStop} of the containing 537 * Activity's lifecycle. 538 */ 539 public void onStop() { 540 mCalled = true; 541 } 542 543 public void onLowMemory() { 544 mCalled = true; 545 } 546 547 /** 548 * Called when the view previously created by {@link #onCreateView} has 549 * been detached from the fragment. The next time the fragment needs 550 * to be displayed, a new view will be created. This is called 551 * after {@link #onStop()} and before {@link #onDestroy()}; it is only 552 * called if {@link #onCreateView} returns a non-null View. 553 */ 554 public void onDestroyView() { 555 mCalled = true; 556 } 557 558 /** 559 * Called when the fragment is no longer in use. This is called 560 * after {@link #onStop()} and before {@link #onDetach()}. 561 */ 562 public void onDestroy() { 563 mCalled = true; 564 } 565 566 /** 567 * Called when the fragment is no longer attached to its activity. This 568 * is called after {@link #onDestroy()}. 569 */ 570 public void onDetach() { 571 mCalled = true; 572 } 573 574 /** 575 * Initialize the contents of the Activity's standard options menu. You 576 * should place your menu items in to <var>menu</var>. For this method 577 * to be called, you must have first called {@link #setHasOptionsMenu}. See 578 * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu} 579 * for more information. 580 * 581 * @param menu The options menu in which you place your items. 582 * 583 * @see #setHasOptionsMenu 584 * @see #onPrepareOptionsMenu 585 * @see #onOptionsItemSelected 586 */ 587 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 588 } 589 590 /** 591 * Prepare the Screen's standard options menu to be displayed. This is 592 * called right before the menu is shown, every time it is shown. You can 593 * use this method to efficiently enable/disable items or otherwise 594 * dynamically modify the contents. See 595 * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu} 596 * for more information. 597 * 598 * @param menu The options menu as last shown or first initialized by 599 * onCreateOptionsMenu(). 600 * 601 * @see #setHasOptionsMenu 602 * @see #onCreateOptionsMenu 603 */ 604 public void onPrepareOptionsMenu(Menu menu) { 605 } 606 607 /** 608 * This hook is called whenever an item in your options menu is selected. 609 * The default implementation simply returns false to have the normal 610 * processing happen (calling the item's Runnable or sending a message to 611 * its Handler as appropriate). You can use this method for any items 612 * for which you would like to do processing without those other 613 * facilities. 614 * 615 * <p>Derived classes should call through to the base class for it to 616 * perform the default menu handling. 617 * 618 * @param item The menu item that was selected. 619 * 620 * @return boolean Return false to allow normal menu processing to 621 * proceed, true to consume it here. 622 * 623 * @see #onCreateOptionsMenu 624 */ 625 public boolean onOptionsItemSelected(MenuItem item) { 626 return false; 627 } 628 629 /** 630 * This hook is called whenever the options menu is being closed (either by the user canceling 631 * the menu with the back/menu button, or when an item is selected). 632 * 633 * @param menu The options menu as last shown or first initialized by 634 * onCreateOptionsMenu(). 635 */ 636 public void onOptionsMenuClosed(Menu menu) { 637 } 638 639 /** 640 * Called when a context menu for the {@code view} is about to be shown. 641 * Unlike {@link #onCreateOptionsMenu}, this will be called every 642 * time the context menu is about to be shown and should be populated for 643 * the view (or item inside the view for {@link AdapterView} subclasses, 644 * this can be found in the {@code menuInfo})). 645 * <p> 646 * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an 647 * item has been selected. 648 * <p> 649 * The default implementation calls up to 650 * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though 651 * you can not call this implementation if you don't want that behavior. 652 * <p> 653 * It is not safe to hold onto the context menu after this method returns. 654 * {@inheritDoc} 655 */ 656 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 657 getActivity().onCreateContextMenu(menu, v, menuInfo); 658 } 659 660 /** 661 * Registers a context menu to be shown for the given view (multiple views 662 * can show the context menu). This method will set the 663 * {@link OnCreateContextMenuListener} on the view to this fragment, so 664 * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be 665 * called when it is time to show the context menu. 666 * 667 * @see #unregisterForContextMenu(View) 668 * @param view The view that should show a context menu. 669 */ 670 public void registerForContextMenu(View view) { 671 view.setOnCreateContextMenuListener(this); 672 } 673 674 /** 675 * Prevents a context menu to be shown for the given view. This method will 676 * remove the {@link OnCreateContextMenuListener} on the view. 677 * 678 * @see #registerForContextMenu(View) 679 * @param view The view that should stop showing a context menu. 680 */ 681 public void unregisterForContextMenu(View view) { 682 view.setOnCreateContextMenuListener(null); 683 } 684 685 /** 686 * This hook is called whenever an item in a context menu is selected. The 687 * default implementation simply returns false to have the normal processing 688 * happen (calling the item's Runnable or sending a message to its Handler 689 * as appropriate). You can use this method for any items for which you 690 * would like to do processing without those other facilities. 691 * <p> 692 * Use {@link MenuItem#getMenuInfo()} to get extra information set by the 693 * View that added this menu item. 694 * <p> 695 * Derived classes should call through to the base class for it to perform 696 * the default menu handling. 697 * 698 * @param item The context menu item that was selected. 699 * @return boolean Return false to allow normal context menu processing to 700 * proceed, true to consume it here. 701 */ 702 public boolean onContextItemSelected(MenuItem item) { 703 return false; 704 } 705} 706