56

I want to set a buttons visibility after the animation is finished.

That's what calls the animation:

android.support.v4.app.FragmentTransaction fAnimation = this.getActivity().getSupportFragmentManager().beginTransaction();
fAnimation.setCustomAnimations(android.R.anim.slide_in_left, R.anim.pull_out_to_left);
if (this.isVisible()) {
    fAnimation.hide(this);
    fAnimation.commit();
}

// code that will be executed when the fragment is gone (after the animation is over)

Is there any way to attach a listener to know when my fragment is gone?

Smi
  • 12,505
  • 9
  • 53
  • 61
Moop
  • 593
  • 1
  • 4
  • 6

5 Answers5

66

The Animators that @nmw implements in his answer were added in API Level 11 and will not work with Fragments as implemented by the Android support library.

To listen to Fragment animation events, I extended the support library's Fragment class and overrode onCreateAnimation, attaching a custom AnimationListener to the returned Animation object:

public class MyFragment extends android.support.v4.app.Fragment {

    private static final String TAG = "MyFragment";

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

        Animation anim = AnimationUtils.loadAnimation(getActivity(), nextAnim);

        anim.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                Log.d(TAG, "Animation started.");
                // additional functionality 
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                Log.d(TAG, "Animation repeating.");
                // additional functionality
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                Log.d(TAG, "Animation ended.");
                // additional functionality
            }
        });

        return anim;
    }
}
Dan J
  • 24,430
  • 17
  • 95
  • 168
mjama
  • 2,600
  • 2
  • 20
  • 24
  • 8
    The listener isn't called when enter is false, because FragmentManagerImpl sets its own listener. Returning it wrapped with an AnimationSet works. – sergio91pt Feb 10 '14 at 18:52
  • @sergio91pt Could you please explain how? – Aman Alam Feb 12 '14 at 03:36
  • 7
    @Sheikh Aman Just create a new AnimationSet, add the animation (which has your listener setted) and return the set. This simply allows FragmentManagerImpl to set another listener without overriding your own. – sergio91pt Feb 20 '14 at 16:31
  • 3
    How is this supposed to work? I'm using this code and it's flickers the animation before going back to the original screen and animating properly. My code is: `Animation anim = AnimationUtils.loadAnimation(getActivity(), nextAnim); AnimationSet animationWrapper = new AnimationSet(false); animationWrapper.addAnimation(anim); ... return animationWrapper;` – Merk Oct 28 '14 at 15:33
  • @Merk thanks! Moved from v13 to v4 frags. I've been wracking my brain over this for way too long. – fahmy Sep 08 '15 at 10:59
  • To anyone following @Merk's suggestion, it creates a bug in the transition animation were animation starts with an unanimated frame causing a flicker at the start. I had to wrack my brain even more over this :/ – fahmy Sep 08 '15 at 15:19
  • @fahmy, about the flickering did you try with `setFillBefore()` / `setFillAfter()` and `setFillEnabled()` ? – WindRider Dec 01 '15 at 16:00
  • Yes, I remember experimenting with those, couldn't get rid of the flicker though. – fahmy Dec 01 '15 at 16:29
  • See my answer to avoid the flickering issue http://stackoverflow.com/a/33904189/2754727 – pnavk May 24 '16 at 22:53
  • 1
    You can avoid the flickering from the @Merk approach by returning the `animation` when it is entering and the `animation set` in its exit – Alberto Méndez Dec 08 '16 at 20:54
  • But the transit and the netxAnim parameters always are zero! – Mohammad Afrashteh Oct 24 '18 at 20:28
  • You should check if(isVisible) before doing something in the fragment because the animation listener also is called when exit – Terranology Aug 17 '19 at 12:53
16

You need to subclass Fragment and override onCreateAnimator, then you can load those animations from XML and attach listeners to them.

E.g.

public class MyFragment extends Fragment
{
    @Override
    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim)
    {
        final int animatorId = (enter) ? R.anim.in_anim : R.anim.out_anim;
        final Animator anim = AnimatorInflater.loadAnimator(getActivity(), animatorId);
        anim.addListener(new AnimatorListenerAdapter()
        {
            @Override
            public void onAnimationStart(Animator animation)
            {
                ...
            }

            @Override
            public void onAnimationEnd(Animator animation)
            {
               ...
            }
        });

        return anim;
   }    
nmw
  • 6,326
  • 3
  • 28
  • 31
  • I tried this, but `onCreateAnimation` is never called. I'm using `Fragment` from support package v4 and it doesn't have `onCreateAnimatior`, only `onCreateAnimation`. Is there any difference? – ffleandro Nov 17 '12 at 18:37
  • Not noticed that before, but I guess that's because Animators are 3.0+ only. Are you setting up the fragment transaction correctly as shown in http://stackoverflow.com/questions/4817900/android-fragments-and-animation ? – nmw Nov 18 '12 at 10:25
  • yes, `FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right); ft.addToBackStack(null); ft.replace(R.id.tabContainer, fragment); ft.commit(); fragmentManager.executePendingTransactions();` – ffleandro Nov 19 '12 at 10:44
  • I also want to do same feature..I am able to get Transaction animation when fragment create but how can give animation while it get finish/destroy?...any luck? – CoDe Dec 03 '12 at 04:50
  • See my answer below for an Android support library-compatible solution. – mjama Mar 14 '13 at 00:24
  • This returns null if one uses setCustomAnimation(int,int,int,int) method of FragmentTransaction class – George Apr 15 '13 at 13:33
  • This solution isn't very compatible with using setCustomAnimation, as @george points out (I assume you wanted to call `super.onCreateAnimator` and get the configured animator). It overrides it, but also adds animation to configuration changes in addition to fragment transitions. Not usable in all use cases. – Nilzor Aug 17 '15 at 13:50
  • I got this to work and stopped the flickering. I just added this: `if (enter) super.onCreateAnimator(transit, enter, nextAnim);` – pnavk Nov 24 '15 at 21:26
  • @nmw `onCreateAnimator` is not overridden method in Fragment as of now.. it is not found in fragment's overridden methods – Kushal Dec 07 '17 at 13:50
  • How to use it in setCustomAnimations? – Mohammad Afrashteh Oct 23 '18 at 17:26
8

Combining the answers above here is a sample I am using successfully with the support library fragments.

Simply extend the MenuFragment and set the listener to get a callback of what to execute afterwards.

public class MenuFragment extends Fragment {

private WeakReference<OnMenuClosedListener> onMenuClosedListener;

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    Animation anim = null;
    if (enter) {
        anim = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_slide_in_top);
    } else {
        anim = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_menu_slide_out_top);
        anim.setAnimationListener(new AnimationListener() {
            @Override public void onAnimationStart(Animation animation) {
            }
            @Override public void onAnimationRepeat(Animation animation) {
            }
            @Override public void onAnimationEnd(Animation animation) {
                onMenuClosed();
            }
        });
    }

    // NOTE: the animation must be added to an animation set in order for the listener
    // to work on the exit animation
    AnimationSet animSet = new AnimationSet(true);
    animSet.addAnimation(anim);

    return animSet;
}

private void onMenuClosed() {
    if (this.onMenuClosedListener != null) {
        OnMenuClosedListener listener = this.onMenuClosedListener.get();
        if (listener != null) {
            listener.onMenuClosed();
        }
    }
}

public void setOnMenuClosedListener(OnMenuClosedListener listener) {
    this.onMenuClosedListener = new WeakReference<MenuFragment.OnMenuClosedListener>(listener);
}

/**
 * Callback for when the menu is closed.
 */
public static interface OnMenuClosedListener {

    public abstract void onMenuClosed();

}

}

Meanman
  • 1,363
  • 18
  • 16
2

Added in API 26 (and in Support Library) you can use

 FragmentTransaction runOnCommit (Runnable runnable);

For example:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_from_left);
ft.show(YourFragment);
ft.commit();
ft.runOnCommit(() -> Your_Action_Here);
  • 2
    I haven't tested this but looking at the docs the runnable will run when the transaction is committed, rather than when an animation has finished. Even if this works it can only be used when the transaction hasn't been added to the backstack - which is a pretty major limitation. – chips Mar 23 '19 at 16:29
  • @Giuseppe this will Immediately be executed once the transaction is committed. Your answer is not correct! – AouledIssa Jun 02 '20 at 14:11
  • 3
    This is not a correct answer. – EpicPandaForce Jan 03 '21 at 05:00
1

I had to do this in Xamarin. My situation was I needed a callback once the fragment animation ended. Here is how I made it work without any flickering (this is C#/Xamarin):

    public override Animation OnCreateAnimation(int transit, bool enter, int nextAnim)
    {
        Animation anim = base.OnCreateAnimation(transit, enter, nextAnim);

        if (anim == null && nextAnim != 0) {
            anim = AnimationUtils.LoadAnimation(Activity, nextAnim);
        }

        anim.SetAnimationListener(this);
        return anim;
    }

    public void OnAnimationEnd(Animation animation)
    {
    }

    public void OnAnimationRepeat(Animation animation)
    {
    }

    public void OnAnimationStart(Animation animation)
    {
    }

Note:

  • The parent fragment has to implement Animation.IAnimationListener
  • You have to return an AnimationSet otherwise the FragmentManager will override your listener and the callbacks won't fire.
pnavk
  • 4,132
  • 1
  • 16
  • 38