Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested fragments disappear during transition animation

Here's the scenario: Activity contains fragment A, which in turn uses getChildFragmentManager() to add fragments A1 and A2 in its onCreate like so:

getChildFragmentManager()
  .beginTransaction()
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

So far, so good, everything is running as expected.

We then run the following transaction in the Activity:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .replace(R.id.fragmentHolder, new FragmentB())
  .addToBackStack(null)
  .commit()

During the transition, the enter animations for fragment B runs correctly but fragments A1 and A2 disappear entirely. When we revert the transaction with the Back button, they initialize properly and display normally during the popEnter animation.

In my brief testing, it got weirder - if I set the animations for the child fragments (see below), the exit animation runs intermittently when we add fragment B

getChildFragmentManager()
  .beginTransaction()
  .setCustomAnimations(enter, exit)
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

The effect I want to achieve is simple - I want the exit (or should it be popExit?) animation on fragment A (anim2) to run, animating the whole container, including its nested children.

Is there any way to achieve that?

Edit: Please find a test case here

Edit2: Thanks to @StevenByle for pushing me to keep trying with the static animations. Apparently you can set animations on a per-op basis (not global to the whole transaction), which means the children can have an indefinite static animation set, while their parent can have a different animation and the whole thing can be committed in one transaction. See the discussion below and the updated test case project.

like image 807
Delyan Avatar asked Feb 15 '13 18:02

Delyan


People also ask

How do you animate a fragment?

To animate the transition between fragments, or to animate the process of showing or hiding a fragment you use the Fragment Manager to create a Fragment Transaction . Within each Fragment Transaction you can specify in and out animations that will be used for show and hide respectively (or both when replace is used).


2 Answers

So there seem to be a lot of different workarounds for this, but based on @Jayd16's answer, I think I've found a pretty solid catch-all solution that still allows for custom transition animations on child fragments, and doesn't require doing a bitmap cache of the layout.

Have a BaseFragment class that extends Fragment, and make all of your fragments extend that class (not just child fragments).

In that BaseFragment class, add the following:

// Arbitrary value; set it to some reasonable default private static final int DEFAULT_CHILD_ANIMATION_DURATION = 250;  @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {     final Fragment parent = getParentFragment();      // Apply the workaround only if this is a child fragment, and the parent     // is being removed.     if (!enter && parent != null && parent.isRemoving()) {         // This is a workaround for the bug where child fragments disappear when         // the parent is removed (as all children are first removed from the parent)         // See https://code.google.com/p/android/issues/detail?id=55228         Animation doNothingAnim = new AlphaAnimation(1, 1);         doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));         return doNothingAnim;     } else {         return super.onCreateAnimation(transit, enter, nextAnim);     } }  private static long getNextAnimationDuration(Fragment fragment, long defValue) {     try {         // Attempt to get the resource ID of the next animation that         // will be applied to the given fragment.         Field nextAnimField = Fragment.class.getDeclaredField("mNextAnim");         nextAnimField.setAccessible(true);         int nextAnimResource = nextAnimField.getInt(fragment);         Animation nextAnim = AnimationUtils.loadAnimation(fragment.getActivity(), nextAnimResource);          // ...and if it can be loaded, return that animation's duration         return (nextAnim == null) ? defValue : nextAnim.getDuration();     } catch (NoSuchFieldException|IllegalAccessException|Resources.NotFoundException ex) {         Log.w(TAG, "Unable to load next animation from parent.", ex);         return defValue;     } } 

It does, unfortunately, require reflection; however, since this workaround is for the support library, you don't run the risk of the underlying implementation changing unless you update your support library. If you're building the support library from source, you could add an accessor for the next animation resource ID to Fragment.java and remove the need for reflection.

This solution removes the need to "guess" the parent's animation duration (so that the "do nothing" animation will have the same duration as the parent's exit animation), and allows you to still do custom animations on child fragments (e.g. if you're swapping child fragments around with different animations).

like image 151
Kevin Coppock Avatar answered Sep 23 '22 18:09

Kevin Coppock


In order to avoid the user seeing the nested fragments disappearing when the parent fragment is removed/replaced in a transaction you could "simulate" those fragments still being present by providing an image of them, as they appeared on the screen. This image will be used as a background for the nested fragments container so even if the views of the nested fragment go away the image will simulate their presence. Also, I don't see loosing the interactivity with the nested fragment's views as a problem because I don't think you would want the user to act on them when they are just in the process of being removed(probably as a user action as well).

I've made a little example with setting up the background image(something basic).

like image 44
user Avatar answered Sep 22 '22 18:09

user