40

I want to animate the removal of fragment.

I tried:

getSupportFragmentManager().beginTransaction()
    .setCustomAnimations(R.anim.push_down_in, R.anim.push_up_out)
    .remove(myFragment)
    .commit();

but the fragment just disappears.

I noticed the out animation only plays with 'replace', so I tried to replace the fragment with an empty Fragment like this:

getSupportFragmentManager()
    .beginTransaction()
    .setCustomAnimations(R.anim.push_down_in, R.anim.push_up_out)
    .replace(viewId, new Fragment())
.commit();

But it still just disappears disappears.

So, how can I animate the removal of fragment?

hugoc
  • 777
  • 1
  • 6
  • 11

13 Answers13

21

I saw this when I was having similar problems and just thought Id drop a quick note.

Rather than creating a dummy fragment in order to replace the existing one I think you should animate the current fragments view. When the animation finishes you can simply remove the fragment.

This is how i did it:

final FragmentActivity a = getSherlockActivity();

if (a != null) {
    //Your animation
    Animation animation = AnimationUtils.loadAnimation(a, R.anim.bottom_out);
    animation.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime));

    //You can use AnimationListener, MagicAnimationListener is simply a class extending it.
    animation.setAnimationListener(new MagicAnimationListener() {
        @Override
        public void onAnimationEnd(Animation animation) {
            //This is the key, when the animation is finished, remove the fragment.
            try {
                FragmentTransaction ft = a.getSupportFragmentManager().beginTransaction();
                ft.remove(RestTimerFragment.this);
                ft.commitAllowingStateLoss();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });

    //Start the animation.  
    getView().startAnimation(animation);
}
Benoit Duffez
  • 11,839
  • 12
  • 77
  • 125
zoltish
  • 2,122
  • 19
  • 37
19

I figured it out.

The exiting view is animated on the canvas of the entering view so if there is no entering canvas there is no canvas for the animation.

To show the animation I had to always use replace and use entering fragments of the same size to those exiting. After the animation is finished I set the view of the new fragments to gone.

hugoc
  • 777
  • 1
  • 6
  • 11
  • I'd love to hear more about this. I'd like to animate the removal of several Fragments, then after some time later (AsyncTask processing) animate in another set of Fragments. Can I simply create a 'dummy' fragment with a transparent view of the same size as the original that I want to animate out? – Dan Devine May 15 '13 at 04:29
  • Yes, that's exactly what I did. – hugoc May 17 '13 at 21:38
  • Is there no other solution? – Archie G. Quiñones Aug 18 '18 at 03:40
14

You could animate the removal by setting this custom animation to the fragmentTransaction

        fragmentTransaction.setCustomAnimations(R.anim.right_in, R.anim.defff,R.anim.defff,R.anim.right_out);

The third and fourth params are for removing the fragment

amalBit
  • 12,041
  • 6
  • 77
  • 94
Antwan
  • 3,837
  • 9
  • 41
  • 62
2

I got inspiration from Zoltish answer , this is my implementation:

1.add this method inside the fragment , it will animate the fragment out of the screen to the left:

public void animateOut()
{
    TranslateAnimation trans=new TranslateAnimation(0,-300*Utils.getDensity(getActivity()), 0,0);
    trans.setDuration(150);
    trans.setAnimationListener(new Animation.AnimationListener() {

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            // TODO Auto-generated method stub
            ((BetsActivty)getActivity()).removeFrontFragmentAndSetControllToBetting();
        }
    });
    getView().startAnimation(trans);
}

The method that inside onAnimationEnd() removes the fragment like this:

getSupportFragmentManager().beginTransaction().
        remove(getSupportFragmentManager().findFragmentById(R.id.fragment_container)).commit();

2.call the animateOut of the fragment from onBack() of the activity.

Cheers

by the way my getDensity() is:

public static int getDensity(Context context)
{
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return (int)metrics.density;
}

with it i calculate the DP value for the current running Device.

Gal Rom
  • 6,221
  • 3
  • 41
  • 33
  • OnAnimationEnd() is broken http://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-called-onanimationstart-works-fine – Kurovsky Sep 16 '15 at 21:48
2

Replacing with an empty fragment, before the point of insertion of the next fragment and also delaying the insertion of the next fragment (by 200ms) so that the exit animation of the blank fragment can play, solved my problem.

This the code to insert an empty fragment with exit animation.

getSupportFragmentManager()
                        .beginTransaction()
                        .setCustomAnimations(R.anim.exit, R.anim.pop_exit)
                        .replace(R.id.fragmentLayout, new Fragment())
                        .commit();

Exit.xml

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

</set>

pop_exit.xml

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

</set>
Ajit
  • 964
  • 10
  • 17
2

I agreed with hugoc and here some code to solve it

public class MyActivity extends Activity {
    //Some thing
    public void Fragment2BackToFragment1(Fragment fragment1, Fragment fragment2) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        animateExit(fragment2);
        ft.replace(R.id.main_content, fragment1, "fragment1");
        ft.commit();
    }
    private void animateExit(Fragment exitFragment) {
        if (exitFragment != null) {
            final View view = exitFragment.getView();
            if (view != null) {
                int anim = R.anim.fade_out;
                Animation animation =
                    AnimationUtils.loadAnimation(getActivity(), anim);
                animation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        view.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                view.setVisibility(View.GONE);
                            }
                        }, 300);
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {


                    }
                });
                view.startAnimation(animation);
            }
        }
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
hungnv39
  • 51
  • 2
2

An easy fix below:

1-Call animate on fragment.getView().

2-Remove fragment inside onAnimationEnd().

final Fragment frag= getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
        frag.getView().animate().alpha(0f).scaleX(0f).scaleY(0f)

                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        getSupportFragmentManager()
                                .beginTransaction()
                                .remove(frag)
                                .commit();
                    }
                }).start();
Community
  • 1
  • 1
Haytham Anmar
  • 552
  • 1
  • 7
  • 8
1

reason as @hugoc said

The exiting view is animated on the canvas of the entering view so if there is no entering canvas there is no canvas for the animation.

To show the animation I had to always use replace and use entering fragments of the same size to those exiting. After the animation is finished I set the view of the new fragments to gone.

below is actual code:

FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.setCustomAnimations(R.anim.slide_in_bottom, R.anim.slide_out_top);
    transaction.hide(removeFragment).add(R.id.fragment_container,addFragment).commit();
    transaction = manager.beginTransaction();
    transaction.remove(removeFragment).commit();
Community
  • 1
  • 1
taotao
  • 747
  • 9
  • 11
0

What enter:
it is new fragment which must be show

What exit:
it is current fragment which must be hide

What popEnter:
it is previous fragment which must be show

What popExit:
it is current fragment which must be hide

For use these animations, you should target them on show or hide transaction commands. Exit animations doesn't work on remove/replace procedures.

0

setCustomAnimations(enter, exit, popEnter, popExit) support enter and exit animation, So set four animations and must keep it before transaction.replace()

Kotlin:

    val manager = supportFragmentManager
    val transaction = manager.beginTransaction()
    transaction.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right ,
            android.R.anim.slide_in_left, android.R.anim.slide_out_right)
    transaction.commit()
Md Imran Choudhury
  • 9,343
  • 4
  • 62
  • 60
0

So the easy way:

When you open a fragment (called from parent Activity):

    FragmentA fragment = new FragmentA();
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.setCustomAnimations(R.anim.slide_up, R.anim.slide_down);
    transaction.add(android.R.id.content, fragment);
    transaction.addToBackStack(null);
    transaction.commit();

Specify enter and exit transactions

transaction.setCustomAnimations(R.anim.slide_up, R.anim.slide_down);

When closing a fragment (called from inside the fragment)

getActivity().getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.slide_up, R.anim.slide_down).remove(this).commit();

Specify enter and exit transactions

setCustomAnimations(R.anim.slide_up, R.anim.slide_down)

0

See different variants of animations in https://developer.android.com/training/basics/fragments/animate.

On any event (button click, time out, etc.) you can write inside a fragment:

parentFragmentManager.beginTransaction()
    .setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right)
    .remove(this)
    .commitAllowingStateLoss()
// parentFragmentManager.popBackStack() - might be needed if the fragment keeps visible after removing.
CoolMind
  • 26,736
  • 15
  • 188
  • 224
0

Just because I spent time on it and I like my extension function: (Kotlin)

fun FragmentManager.removeByTagAnimated(tag: String, animation: Int){
    findFragmentByTag(tag)?.let { fragToRemove ->
        val anim = AnimationUtils.loadAnimation(fragToRemove.context, animation).apply {
            setAnimationListener(object : Animation.AnimationListener {
                override fun onAnimationStart(animation: Animation?) {}
                override fun onAnimationRepeat(animation: Animation?) {}

                override fun onAnimationEnd(animation: Animation?) {
                    commit {
                        remove(fragToRemove)
                    }
                }
            })
        }
        fragToRemove.view?.startAnimation(anim)
    }
}

Animation is an R.anim resource like in this page

Just use supportFragmentManager.removeByTagAnimated(MY_FRAGMENT_TAG, R.anim.my_exit_animation) in your Activity.

Joozd
  • 501
  • 2
  • 14