24

I have below code for setting enter and exit animations for fragments.

final FragmentManager manager = getSupportFragmentManager();
final FragmentTransaction ft = manager.beginTransaction();
    ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
    ft.replace(R.id.container, fragment, tag);
    ft.addToBackStack(tag);
    ft.commitAllowingStateLoss();

slide_in_left.xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate android:fromXDelta="-100%"
    android:toXDelta="0%"
    android:duration="@integer/slide_anim_duration" />

</set>

Used same method for all other animations. Works fine in all other versions, but the problem is in Lollipop only. As we look in to the function
setCustomAnimations (int enter, int exit, int popEnter, int popExit) doc-link ,
enter and exit works fine, but popEnter and popExit fails in Lollipop.
Please guide me about what went wrong and provide a compatible solution that works from Android 2.3 to 5.1

Update:

Observing closely,it seems that the animation is there, but the duration has no effect.

Nizam
  • 5,698
  • 9
  • 45
  • 57
  • Have you tried other animations to check if the problem is in this specific `slide_in_left.xml` one? – Ricardo Apr 21 '15 at 07:27
  • Also, have you tested on different devices? Does the problem happen on an emulator? – Ricardo Apr 21 '15 at 07:29
  • @Ricardo Yes, I'm using so many animations like that one. All are working fine except with `popEnter` and `popExit` in lollipop. Problem is in devices with Lollipop. Didn't try emulator. – Nizam Apr 21 '15 at 07:31
  • I have almost the same kind of transaction in a project of mine that is working fine. The only two things different are: 1) I'm passing `null` to `addToBackStack()` and 2) I'm using `commit()` instead of `commitAllowingStateLoss()`. – Ricardo Apr 21 '15 at 07:44
  • Works fine on API 22 and API 16. The problem is device specific, or could be connected with the unlisted animation files. – Simas Apr 21 '15 at 20:54

5 Answers5

3

Fragment animations are broken even before L - there are some issues when changing screen orientation.

Below solution fixes both L issues and earlier orientation change issues. Use it as your base Fragment class.

public abstract class AnimatedSupportFragment extends DaggerFragment {

    private static final String STATE_ENTER_ANIM = "STATE_ENTER_ANIM";
    private static final String STATE_EXIT_ANIM = "STATE_EXIT_ANIM";
    private static final String STATE_POP_ENTER_ANIM = "STATE_POP_ENTER_ANIM";
    private static final String STATE_POP_EXIT_ANIM = "STATE_POP_EXIT_ANIM";
    private static final String STATE_CHANGING_CONFIGURATIONS = "STATE_CHANGING_CONFIGURATIONS";

    private @AnimRes int mEnter = 0;
    private @AnimRes int mExit = 0;
    private @AnimRes int mPopEnter = 0;
    private @AnimRes int mPopExit = 0;
    private boolean mIsChangingConfigurations = false;

    public void setCustomAnimations(@AnimRes int enterAnim,
                                    @AnimRes int exitAnim,
                                    @AnimRes int popEnterAnim,
                                    @AnimRes int popExitAnim) {
        mEnter = enterAnim;
        mExit = exitAnim;
        mPopEnter = popEnterAnim;
        mPopExit = popExitAnim;
    }

    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mEnter = savedInstanceState.getInt(STATE_ENTER_ANIM);
            mExit = savedInstanceState.getInt(STATE_EXIT_ANIM);
            mPopEnter = savedInstanceState.getInt(STATE_POP_ENTER_ANIM);
            mPopExit = savedInstanceState.getInt(STATE_POP_EXIT_ANIM);
            mIsChangingConfigurations = savedInstanceState.getBoolean(STATE_CHANGING_CONFIGURATIONS);
        }
    }

    @Override public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_ENTER_ANIM, mEnter);
        outState.putInt(STATE_EXIT_ANIM, mExit);
        outState.putInt(STATE_POP_ENTER_ANIM, mPopEnter);
        outState.putInt(STATE_POP_EXIT_ANIM, mPopExit);
        outState.putBoolean(STATE_CHANGING_CONFIGURATIONS, mIsChangingConfigurations);
    }

    @Override public void onResume() {
        super.onResume();
        mIsChangingConfigurations = false;
    }

    @Override public void onPause() {
        super.onPause();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mIsChangingConfigurations = getActivity().isChangingConfigurations();
        }
    }

    @SuppressLint("NewApi")
    @Override public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {

        // This implementation fixes loosing transition animations on orientation changes:
        // @see http://stackoverflow.com/questions/8837408/fragment-lost-transition-animation-after-configuration-change
        // @see https://code.google.com/p/android/issues/detail?id=25994&can=4&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            // Do not support animations PRE-3.0, isChangingConfigurations() is not available!
            return null;
        }

        try {
            int anim;
            if (mIsChangingConfigurations) {
                // Recreating after change configuration, we don't want to play animation
                anim = 0;
            } else if (nextAnim != 0) {
                // Animation available (not lost) -> play it!
                anim = nextAnim;
            } else {
                // Animation probably lost - load anim saved in fragment state.
                // enter = we're about to play popEnterAnim, in other case popExitAnimation
                anim = enter ? mPopEnter : mPopExit;
            }
            if (anim != 0) {
                return AnimationUtils.loadAnimation(getActivity(), anim);
            }
        } catch (Exception ignore) {}

        return null;
    }
}
jskierbi
  • 1,635
  • 1
  • 14
  • 22
  • Are the values for STATE_ENTER_ANIM etc arbitrary or did you get them from looking at the android source? From the lack of upvotes I can only assume people are not using fragment transition animations that much, nice work. – enl8enmentnow Nov 05 '15 at 08:38
  • I'm using this code in many projects and it works like a charm :) What are the symptoms? What is your use case? How did you create your fragment transaction? – jskierbi Feb 19 '16 at 09:35
1

you can additionally try onCreateAnimator in all the fragments..or the base fragment that they are extending...

@target_api 20 or higher

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

Note both above animators are pre defined and R there is android.R

Hope this helps cheers!

0

If you put the duration to 2000 you will see the animation, but I think it's too slow. This is my code:

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="-100%p"
    android:toXDelta="0"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:duration="2000" />

You can put less duration, but you will see less animation.

0

I find an other solution, it not makes exactly the same animation, but is very similar, I like this animation more than the traditional slide this is my code for do the animation:

     private Fragment fragment=new Fragment();
 private FragmentManager fragmentManager=getSupportFragmentManager();


private void replaceFragmentWithAnimation(){
    FragmentTransaction ft = fragmentManager.beginTransaction();
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fragment != null) {
            Slide slideLeft = new Slide(Gravity.RIGHT);
            slideLeft.setDuration(300);
            fragment.setExitTransition(slideLeft);
        }

        fragment = CalendarFragment.newInstance(selectedDays, plusMonths);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Slide slideRight = new Slide(Gravity.RIGHT);
            slideRight.setDuration(350);
            fragment.setEnterTransition(slideRight);

        } else {
           ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right);
        }
        ft.replace(calendarDaysContFL.getId(), fragment);
        ft.commit();
    }
0

Here is the working solution: Modified version of @jskierbi answer.

import android.support.v4.app.Fragment;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;


/**
 * @author : Pedramrn@gmail.com Created on: 2016-02-19
 */
public class SupportBaseFragment extends Fragment {

private boolean mIsChangingConfigurations = false;
private static final String STATE_CHANGING_CONFIGURATIONS = "STATE_CHANGING_CONFIGURATIONS";

@Override
public void onResume() {
    super.onResume();
    mIsChangingConfigurations = false;
}

@Override
public void onPause() {
    super.onPause();
    mIsChangingConfigurations = getActivity().isChangingConfigurations();
}


@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if (mIsChangingConfigurations)
        return null;
    if (enter)
        return AnimationUtils.loadAnimation(getContext(), nextAnim == 0 ? R.anim.enter_pop : nextAnim);
    else
        return AnimationUtils.loadAnimation(getContext(), nextAnim == 0 ? R.anim.exit_pop : nextAnim);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        mIsChangingConfigurations = savedInstanceState.getBoolean(STATE_CHANGING_CONFIGURATIONS);
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(STATE_CHANGING_CONFIGURATIONS, mIsChangingConfigurations);
}

enter.xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="100%"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:toXDelta="0"/>
</set>

enter_pop.xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="-100%"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:toXDelta="0"/>
</set>

exit.xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="-100%"/>
</set>

exit_pop.xml

<?xml version="1.0" encoding="utf-8"?>
<set>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="100%"/>
</set>

Usage:

getSupportFragmentManager()
                .beginTransaction()
                .setCustomAnimations(R.anim.enter, R.anim.exit,
                        R.anim.enter_pop, R.anim.exit_pop)
                .replace(R.id.fragment_container, myFancyFragment)
                .addToBackStack(null)
                .commit();
M. Reza Nasirloo
  • 16,434
  • 2
  • 29
  • 41