Pop the fragment backstack without playing the Pop

2019-01-07 07:57发布

I push a fragment on the fragment stack using the following code:

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right,
     R.anim.slide_in_left, R.anim.slide_out_left);
fragmentTransaction.replace(getId(), newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

This way, when the fragment stack is popped, e.g. by pressing the back button, a fragment pop animation is played. However, there are situations in which i would like to pop the fragment backstack without showing this animation, e.g. because I just returned from another activity and want to display the previous fragment at once, without animation.

An example navigation could look like this:

  • The user is on the start screen with the root fragment
  • He selects an item on the root fragment which then displays a new fragment to show details of that item. It does so using a fragment transaction that sets animations both for the push and the pop case (so when the user presses the back button, the transition is animated)
  • From this fragment he starts an activity which (for whatever reason) deletes the item that was just shown
  • When this activity finishes, I would like to return to the root fragment without showing the "pop animation" of the "detail fragment"

Is there a way to pop the fragment backstack without playing the specified pop animation?

10条回答
我只想做你的唯一
2楼-- · 2019-01-07 08:03

This is fairly easy to achieve through overridePendingTransition(int enterAnim, int exitAnim) with both 0 for no animation.

FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
    fm.popBackStack();
    overridePendingTransition(0, 0);
}
查看更多
Melony?
3楼-- · 2019-01-07 08:03

Override this in the fragment that you want to pop without animation and still keep the animation when you enter

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter){
        Animation a = new Animation() {};
        a.setDuration(0);
        return a;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}
查看更多
看我几分像从前
4楼-- · 2019-01-07 08:03

Reply to Geoff and plackemacher comment.

You can try to remove all views from this Fragment. Then fragment will show but it should be transparent.

Remove all-1 (I use navigate drawer so drawer fragment should stay) fragment:

    int size = fragmentsList.size ()-1;

    FragmentTransaction transaction = fragmentManager.beginTransaction ();
    transaction.setTransition (FragmentTransaction.TRANSIT_NONE);

    Fragment fragment;

    for (int i = size ; i > 0 ; i--)
    {
        fragment = fragmentsList.get (i);
        if(fragment != null)
        {
            View viewContainer = fragment.getView ();
            if (viewContainer != null)
            {
                ((ViewGroup) viewContainer).removeAllViews ();
            }
            transaction.remove (fragment);
        }
    }

    size = fragmentManager.getBackStackEntryCount ();

    for (int i = 0; i < size  ; i++)
    {
        fragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

Sorry for my English

查看更多
萌系小妹纸
5楼-- · 2019-01-07 08:04

This is a follow-up to @Geoff's excellent answer, but fitted for a more dynamic and real-live scenario.

I imagined this being a nice little post, but I realize now that it got a little out of hand. However, the code is all there and I find it really useful, though it covers a lot more than just how to disable transition animations.

Usually, when I work with Fragments I like to have a BaseFragment that attaches to a BaseActivityCallback. This BaseActivityCallback can be used by the my Fragments to add a new Fragment on top of itself, or even to pop Fragments beneath it, hence the desire to disable pop animations -- or pop silently:

interface BaseActivityCallback
{
    void addFragment ( BaseFragment f, int containerResId );
    void popFragment ( boolean silently );
}

class BaseActivity extends android.support.v4.app.FragmentActivity implements BaseActivityCallback
{
    public void addFragment ( BaseFragment f, int containerResId )
    {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.enter, R.anim.pop_exit); // http://stackoverflow.com/a/17488542/2412477
        ft.addToBackStack(DEFAULT_FRAGMENT_STACK_NAME);
        ft.replace(containerResId, fragment);
        ft.commitAllowingStateLoss();
    }        

    public void popFragment ( boolean silently )
    {
        FragmentManager fm = getSupportFragmentManager();                
        if ( silently ) {
            int count = fm.getFragments().size();
            BaseFragment f = (BaseFragment)fm.getFragments().get(count-1);
            f.setDisableTransitionAnimations(true);
        }
        fm.popBackStackImmediate();
    }
}

public abstract class BaseFragment extends android.support.v4.app.Fragment
{
    private static final String TAG = "BaseFragment";
    private final String STATE_DISABLE_TRANSITION_ANIMATIONS = TAG+".stateDisableTransitionAnimations";

    protected BaseActivityCallback baseActivityCallback;
    private boolean disableTransitionAnimations;    

    @Override
    public void onCreate ( @Nullable Bundle savedInstanceState )
    {
        super.onCreate(savedInstanceState);
        disableTransitionAnimations = (savedInstanceState==null ? false : savedInstanceState.getBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, false));
    }

    @Override
    public void onAttach ( Context context )
    {
        super.onAttach(context);
        baseActivityCallback = (BaseActivityCallback)context;
    }

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

    @Override
    public Animation onCreateAnimation ( int transit, boolean enter, int nextAnim )
    {
        if ( disableTransitionAnimations ) {
            Animation nop = new Animation(){};
            nop.setDuration(0);
            return nop;
        }
        return super.onCreateAnimation(transit, enter, nextAnim);
    }

    public void setDisableTransitionAnimations ( boolean disableTransitionAnimations )
    {
        this.disableTransitionAnimations = disableTransitionAnimations; // http://stackoverflow.com/a/11253987/2412477
    }
}

Now you can create your MainActivity and have that show a Fragment1 which can add another Fragment2 which may in turn pop Fragment1 silently:

public class MainActivity extends BaseActivity
{
    protected void onCreate ( Bundle savedInstanceState )
    {
        setContentView(R.layout.main_activity);
        ...
        if ( getSupportFragmentManager().getFragments() != null && !getSupportFragmentManager().getFragments().isEmpty() ) {

            addFragment( FragmentA.newInstance(), R.id.main_activity_fragment_container );
        }
    }
    ...
}

public class FragmentA extends BaseFragment
{
    public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_a, container, false);
        ...
        root.findViewById(R.id.fragment_a_next_button)
            .setOnClickListener( new View.OnClickListener() {
                 public void onClick ( View v ) {
                      baseActivityCallback.addFragment( FragmentB.newInstance(), R.id.main_activity_fragment_container );
                 }
             });
    }
}

public class FragmentB extends BaseFragment
{
    public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_b, container, false);
        ...
        root.findViewById(R.id.fragment_b_pop_silently_button)
            .setOnClickListener( new View.OnClickListener() {
                 public void onClick ( View v ) {
                      baseActivityCallback.popFragment( true );
                 }
             });
    }
}
查看更多
小情绪 Triste *
6楼-- · 2019-01-07 08:08

So Warpzit was on the right track, he just didn't address your specific issue too well. I came across the exact same issue and here is how I solved it.

First I created a static boolean variable (for simplicity's sake, lets put it in the FragmentUtils class)...

public class FragmentUtils {
    public static boolean sDisableFragmentAnimations = false;
}

Then, in EVERY fragment you have, you need to override the onCreateAnimation method...

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if (FragmentUtils.sDisableFragmentAnimations) {
        Animation a = new Animation() {};
        a.setDuration(0);
        return a;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

Then, when you need to clear the backstack from your activity simply do the following...

public void clearBackStack() {
    FragmentUtils.sDisableFragmentAnimations = true;
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    FragmentUtils.sDisableFragmentAnimations = false;
}

And voila, a call to clearBackStack() will drop you back into the root fragment without any transition animations.

Hopefully the big G will add a less stupid way of doing this in the future.

查看更多
The star\"
7楼-- · 2019-01-07 08:09

Just use another overloaded method of setCustomAnimation() and in which do not set the R.anim.slide_out and that will solve your problem

Cheers :)

查看更多
登录 后发表回答