Home » Android » android – Fragment lost transition animation after configuration change

android – Fragment lost transition animation after configuration change

Posted by: admin April 23, 2020 Leave a comment

Questions:

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}}
    [email protected]
    [email protected]
    mNextAnim=17498112
    [email protected]
    [email protected]
Added Fragments:
  #0: SimpleFragment{408883b0 #0 id=0x7f050000 simple}
Back Stack:
  #0: [email protected]
    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: [email protected]
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}}
    [email protected]
    [email protected]
    [email protected]
    [email protected]
Added Fragments:
  #0: SimpleFragment{40878858 #0 id=0x7f050000 simple}
Back Stack:
  #0: [email protected]
    mName=simple mIndex=0 mCommitted=false
    Operations:
      Op #0:
        cmd=1 fragment=SimpleFragment{40878858 #0 id=0x7f050000 simple}
Back Stack Indices:
  #0: [email protected]
FragmentManager misc state:
  mCurState=5 mStateSaved=false mDestroyed=false
How to&Answers:

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.

Answer:

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.

Answer:

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.

Answer:

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);
    }
}

Answer:

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