How to animate fragment removal

2019-02-01 18:49发布

问题:

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?

回答1:

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.



回答2:

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


回答3:

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



回答4:

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>


回答5:

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.



回答6:

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


回答7:

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


回答8:

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.



回答9:

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


回答10:

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()


回答11:

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)