31
votes

I'm inserting Fragments into the Activity using this code:

public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    FragmentManager fm = getFragmentManager();
    String tag = "simple";

    Fragment fr = fm.findFragmentByTag(tag);
    if (fr == null)
    {
        SimpleFragment simpleFragment = new SimpleFragment(); 
        FragmentTransaction transaction = fm.beginTransaction();
        transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out,
                                        android.R.animator.fade_in, android.R.animator.fade_out);
        transaction.add(R.id.main_layout, simpleFragment, tag);
        transaction.addToBackStack(tag);
        transaction.commit();
    }
}

Fragments code is:

public class SimpleFragment extends ListFragment 
{
    @Override
    public void onActivityCreated(Bundle savedInstanceState) 
    {
        super.onActivityCreated(savedInstanceState);
        getView().setBackgroundColor(Color.YELLOW);
    }
}

When I pop the fragment from backstack via Back button just after launching, then everything is fine and I can see fade out animation. But if I rotate device and press Back button then fragment disappears without animation.

Is this Android behavior or I'm doing something wrong?

EDIT: It seems that after rotation FragmentManager didn't restores animations (enterAnim, exitAnim, popEnterAnim and popExitAnim) for BackStackEntry.

FragmentManager dump (without rotation):

Active Fragments in 4087d668:
  #0: SimpleFragment{408883b0 #0 id=0x7f050000 simple}
    mFragmentId=#7f050000 mContainerId#=7f050000 mTag=simple
    mState=4 mIndex=0 mWho=android:fragment:0 mBackStackNesting=1
    mAdded=true mRemoving=false mResumed=true mFromLayout=false mInLayout=false
    mHidden=false mDetached=false mRetainInstance=false mRetaining=false mHasMenu=false
    mFragmentManager=FragmentManager{4087d668 in ListViewFragmentsActivity{4087d588}}
    mImmediateActivity=my.app.ListViewFragmentsActivity@4087d588
    mActivity=my.app.ListViewFragmentsActivity@4087d588
    mNextAnim=17498112
    mContainer=android.widget.RelativeLayout@408876d8
    mView=android.widget.FrameLayout@40888a70
Added Fragments:
  #0: SimpleFragment{408883b0 #0 id=0x7f050000 simple}
Back Stack:
  #0: android.app.BackStackRecord@408884b8
    mName=simple mIndex=0 mCommitted=true
    mEnterAnim=#10b0000 mExitAnim=#10b0001
    Operations:
      Op #0:
        cmd=1 fragment=SimpleFragment{408883b0 #0 id=0x7f050000 simple}
    enterAnim=17498112 exitAnim=17498113
    popEnterAnim=17498112 popExitAnim=17498113
Back Stack Indices:
  #0: android.app.BackStackRecord@408884b8
FragmentManager misc state:
  mCurState=5 mStateSaved=false mDestroyed=false

FragmentManager dump (after rotation):

Active Fragments in 40877f38:
  #0: SimpleFragment{40878858 #0 id=0x7f050000 simple}
    mFragmentId=#7f050000 mContainerId#=7f050000 mTag=simple
    mState=4 mIndex=0 mWho=android:fragment:0 mBackStackNesting=1
    mAdded=true mRemoving=false mResumed=true mFromLayout=false mInLayout=false
    mHidden=false mDetached=false mRetainInstance=false mRetaining=false mHasMenu=false
    mFragmentManager=FragmentManager{40877f38 in ListViewFragmentsActivity{40877e58}}
    mImmediateActivity=my.app.ListViewFragmentsActivity@40877e58
    mActivity=my.app.ListViewFragmentsActivity@40877e58
    mContainer=android.widget.RelativeLayout@4087ed50
    mView=android.widget.FrameLayout@4087fc00
Added Fragments:
  #0: SimpleFragment{40878858 #0 id=0x7f050000 simple}
Back Stack:
  #0: android.app.BackStackRecord@40878a78
    mName=simple mIndex=0 mCommitted=false
    Operations:
      Op #0:
        cmd=1 fragment=SimpleFragment{40878858 #0 id=0x7f050000 simple}
Back Stack Indices:
  #0: android.app.BackStackRecord@40878a78
FragmentManager misc state:
  mCurState=5 mStateSaved=false mDestroyed=false
5
I also am having this same problem. Have you had any luck figuring it out?khendricks
@khendricks No, and you?Max
Unfortunately not. I am shocked that there isn't a lot of other people complaining about this. I created a bug report for this issue. code.google.com/p/android/issues/…khendricks
@khendricks, try Support4Demos sample project given in provided with Android api. while running u can observe/check Fragment->Custom Animation, in code observe file FragmentCustomAnimationSupport.java. Even if you change orientation application doesn't crash and even Animation works fine. Plz let me know if it works for you.Chanchal Shelar
@khendricks no it shows the exact behavior he's describing. When you go forward it works fine but when go back after orientation change it doesn't show the animation. The reason it never has been an issue for me is we always sets the orientation to portrait or landscape.Warpzit

5 Answers

10
votes

As a workaround for this you can use onCreateAnimator/onCreateAnimation methods in your fragments.

For example for native fragments implementation:

@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    if (enter) {
        return AnimatorInflater.loadAnimator(getActivity(), R.animator.slide_in_top);
    } else {
        return AnimatorInflater.loadAnimator(getActivity(), R.animator.fade_out);
    }
}

The same technique for support library fragments with Animation instead. In this case you also have more control over how would you like to play animation depending upon fragment state and/or arguments.

2
votes

Okay so this is a bug which also is a problem for native library (not only support library).

The only workaround I can suggest is to create your own back stack and then handle onBack with your own custom implementation setting the right animation as you go back through your own stack.

1
votes

An alternative suggestion to work around this issue is to download the source for the support library and make the change I suggested in the defect (http://code.google.com/p/android/issues/detail?id=25994) yourself, of course this means maintaining a copy of the support library yourself and not being able to use the native support, however that depends on how important this issue is for you.

1
votes

You can use onCreateAnimation plus AnimationUtils for each fragment instead of transaction.setCustomAnimations(..). Also to skip animation during restoring, consider about boleean flag.

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    mIsRestoring = savedInstanceState != null;
    ...
}

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if (mIsRestoring) {
        mIsRestoring = false;
        return null;
    }
    if (enter) {
        return AnimationUtils.loadAnimation(getContext(), R.anim.enter_from_right);
    } else {
        return AnimationUtils.loadAnimation(getContext(), R.anim.exit_to_left);
    }
}
0
votes

This bug is fixed few days ago in new support library 23.3.0. https://code.google.com/p/android/issues/detail?id=25994#c36