Tuesday, June 12, 2012

Fragment Transactions Reference

I recently found myself looking at Fragment transactions and wondering precisely what each Fragment state meant.  I vaguely had an idea of what added, attached, shown, hidden, detached and removed meant, but not the specific behaviors of each.  I spent a morning looking through Android's code to better understand what happens to Fragments.

Here's a reference manual for what I learned.  I looked into two aspects of Fragments: fragment states, and animations in transactions.  This research was done primarily on the compatibility library.  It's possible that some of the details below will differ for the 3.x+ implementation of Fragments.

Fragment States

All of the below support animations.  Terminology note: when I say "configuration change", I mean when the device changes configuration (for example, from portrait to landscape) and the Activity must be destroyed/recreated.

FragmentTransaction.add()/remove() - These add and remove a Fragment from the FragmentManager.  By default, it will begin showing the Fragment as well.  During the transaction the returns of Fragment.isAdded() and Fragment.isRemoved() will be modified.  These states are preserved on configuration change, so a Fragment that's added will remain added and can be retrieved again from the FragmentManager.  Since a removed Fragment is no longer part of the FragmentManager, it will not be retrievable after a configuration change either.

FragmentTransaction.attach()/detach() - These attach/detach a Fragment from the Activity (e.g., make it an active Fragment that can be seen).  During the transaction the returns of Fragment.isAdded() and Fragment.isDetached() will be changed.  These states are preserved on configuration change, which means that (unlike removing a Fragment) it is possible to detach a Fragment but then retrieve it later after configuration change (even if it's not actively being displayed).  Even in a detached state, the Fragment runs through all lifecycle methods (except the ones that are related to being attached to an Activity) - for example, onSaveInstanceState() will still be called even if the Fragment is detached (which is quite useful for maintaining state).

FragmentTransaction.show()/hide() - These show or hide a Fragment (in particular, it calls View.setVisibility() on the Fragment's root View).  During the transaction the returns of Fragment.isHidden() will be modified.  Showing/hiding Fragments states are NOT maintained on configuration change, meaning that if you hide a Fragment it will reappear on config change (for example, if the user rotates the screen).

FragmentTransaction.replace() - Theoretically it is the exact same as removing all Fragments associated with a particular View, then adding a new Fragment in its place.  I did not research this much (since I'm more interested in manual control of which Fragments are shown).

Fragment Animations

There are three different ways to animate Fragment transactions.  These are ordered by precedence (in other words, the first method, if available, will be used; then the second, then the last).

One thing to note between the compatibility library and the standard Fragment library is that the standard library uses property animators, whereas the compatibility library uses tween Animations.

1. Override your Fragment's onCreateAnimator() method and return an animation.  This is the only way to dynamically generate an animation in code (the other two methods rely on pre-configured animations from your resources directory).  In the compatibility library, the method is onCreateAnimation() (since the compatibility library uses animations instead of animators).

2. Call FragmentTransaction.setCustomAnimations(), referencing either animators or animations (depending on whether you're using the compatibility library or not).  What is interesting is that setCustomAnimations() affects all fragment transitions added to the transaction after it is called.  So you need to call setCustomAnimations() before you want it used, and you can actually setup multiple different custom animations for each part of a transaction (with a call to setCustomAnimations() before each add()/remove()/attach()/detach()/show()/hide()/replace()).

3. Setup a style that defines window animations (from this style), then call FragmentTransaction.setTransition() and FragmentTransaction.setTransitionStyle() to specify that style and target the correct animation.

#3 works different in the compatibility library.  FragmentTransaction.setTransitionStyle() is completely ignored.  Instead, it uses the specified transition to select a stock animation.  So if you want custom animations in the compatibility library, you must use #1 or #2.

2 comments:

  1. Hey Daniel Great Post! thanks,
    one question.... for me it is working great calling 2 times setCustomAnimations With a tweenAnimations as you mention, but i would like to know how do you use the First approach(Overwrite animation fragment method) i read doc about OncreateAnimation or Animator, but im not able to see how to do it this way.
    here is code i used for 2 approach...
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.setCustomAnimations(R.anim.push_up_in,R.anim.push_up_out);
    ft.commit();
    ft = getSupportFragmentManager().beginTransaction();
    ft.setCustomAnimations(R.anim.push_up_in,R.anim.push_up_out);
    if (fragment1.isHidden()) {
    ft.show(fragment1);

    } else {
    ft.hide(fragment1);

    }
    ft.commit();

    ReplyDelete
  2. Thanks, Daniel. Very helpful to understand FragmentTransaction and states of Fragment. :)

    ReplyDelete