弹出片段backstack而不播放Pop-Animation

时间:2012-02-08 13:32:25

标签: android android-fragments android-animation

我使用以下代码在片段堆栈上推送片段:

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

这样,当弹出片段堆栈时,例如,通过按后退按钮,播放片段弹出动画。但是,有些情况下我想弹出片段后台而不显示这个动画,例如因为我刚从另一个活动返回,想要一次显示前一个片段,没有动画。

示例导航可能如下所示:

  • 用户在开始屏幕上显示根片段
  • 他选择根片段上的一个项目,然后显示一个新片段以显示该项目的详细信息。它使用片段事务来设置动画和弹出案例的动画(因此当用户按下后退按钮时,过渡动画)
  • 他从这个片段开始一个活动(无论出于何种原因)删除刚刚显示的项目
  • 当此活动结束时,我想返回根片段,而不显示“细节片段”的“流行动画”

有没有办法弹出片段后台而不播放指定的流行动画?

12 个答案:

答案 0 :(得分:85)

所以Warpzit走在正确的轨道上,他只是没有很好地解决你的具体问题。我遇到了完全相同的问题,这就是我如何解决它。

首先我创建了一个静态布尔变量(为简单起见,让它放在FragmentUtils类中)...

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

然后,在你拥有的每个片段中,你需要覆盖onCreateAnimation方法......

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

然后,当您需要从活动中清除背斜时,只需执行以下操作......

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

瞧,调用clearBackStack()会让你回到根片段而没有任何过渡动画。

希望G大将在未来添加一种不那么愚蠢的方式。

答案 1 :(得分:6)

所以对于以下工作的支持库:

在应具有自定义弹出动画的片段中,您可以使用自己的自定义动画覆盖onCreateAnimation。您可以根据自己的需要获取并设置某种参数。可能需要做一些额外的工作才能使它与常规片段一起工作。

以下是我覆盖它并更改设置持续时间的示例:

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    Animation anim = (Animation) super.onCreateAnimation(transit, enter, nextAnim);
    if(!enter) {
        if(anim != null) {
            anim.setDuration(0); // This doesn't seem to be called.
            return anim;
        } else {
            Animation test = new TestAnimation();
            test.setDuration(0);
            return test;
        }
    }
    return anim;
}

private class TestAnimation extends Animation {

}

答案 2 :(得分:5)

  

用户在开始屏幕上显示根片段

假设根片段包含在活动A中。

  

他选择根片段上的一个项目,然后显示一个新片段以显示该项目的详细信息。它使用片段事务来设置推送和弹出案例的动画(因此当用户按下后退按钮时,过渡动画)

事务被添加到后台堆栈。这意味着当从细节片段按下后退按钮时,弹出过程将被动画化。

  

从这个片段开始,他开始一个活动(无论出于何种原因)删除刚刚显示的项目。

让我们说它是活动B

  

当这个活动结束时,我想返回根片段,而不显示“细节片段”的“流行动画”

获得此行为的一种方法是在活动B中执行此操作:

Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();

这将启动活动A根据documentation将其重置为其根状态。(查看该部分中的最后一段说明“此启动模式也可与FLAG_ACTIVITY_NEW_TASK一起使用,效果良好: ......“)

使用此配置,动画将以默认情况显示,而在特殊情况下,您可以使用以下方式控制动画:

intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

在没有任何动画的情况下开始新活动。如果你想要任何动画,你可以使用overridePendingTransition方法。

答案 3 :(得分:1)

所以,我想建议对@ Geoff的答案做一点改动。

我没有使用全局静态布尔值,而是使用本地非静态布尔值。这就是我想出来的。

创建界面

public interface TransitionAnimator {
    void disableTransitionAnimation();
    void enableTransitionAnimation();
}

使片段实现该接口。

public class MyFragment extends Fragment implements TransitionAnimator {

    private boolean mTransitionAnimation;

    @Override
    public void disableTransitionAnimation() {
        mTransitionAnimation = false;
    }

    @Override
    public void enableTransitionAnimation() {
        mTransitionAnimation = true;
    }

    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        Animation result;
        if (!mTransitionAnimation) {
            Animation dummyAnimation = new Animation() {
            };
            dummyAnimation.setDuration(0);
            result = dummyAnimation;
        } else {
            result = super.onCreateAnimation(transit, enter, nextAnim);
        }
        return result;
    }

然后,当您想要禁用片段的过渡动画时,只需执行

if (fragment instanceof TransitionAnimator) {
    ((TransitionAnimator) fragment).disableTransitionAnimation();
}

启用它们,只需执行

if (fragment instanceof TransitionAnimator) {
    ((TransitionAnimator) fragment).enableTransitionAnimation();
}

如果要对片段管理器中的所有片段执行相同操作,只需执行

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
    if (fragment instanceof TransitionAnimator) {
        // disable animations
        ((TransitionAnimator) fragment).disableTransitionAnimation();
    }
}

非常相似,但没有静态字段。

答案 4 :(得分:0)

使用setCustomAnimation()的另一种重载方法,其中不设置 R.anim.slide_out 这将解决您的问题

干杯:)

答案 5 :(得分:0)

在回答你的问题之前,我需要自己提问。

在第二个活动的onBackPressed()方法中,您可以访问第一个活动的backstack吗?

如果是,那么你可以调用popBackStackImmediate(String trnaisiotnName,int inclusive),它将从backstack中删除片段转换,你不必担心动画。

我假设你可以访问之前活动的后台,否则这不会起作用

答案 6 :(得分:0)

这很容易通过overridePendingTransition(int enterAnim, int exitAnim)同时使用0来实现无动画。

FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
    fm.popBackStack();
    overridePendingTransition(0, 0);
}

答案 7 :(得分:0)

这是对@ Geoff的优秀答案的后续跟进,但适用于更具动态性和实时性的场景。

我想象这是一个不错的小帖子,但我现在意识到它有点失控。但是,代码就在那里,我觉得它非常有用,虽然它不仅仅涉及如何禁用转换动画。

通常,当我使用Fragments时,我喜欢将BaseFragment附加到BaseActivityCallback。我的片段可以使用这个BaseActivityCallback在其自身之上添加一个新的片段,甚至可以在其下面弹出片段,因此需要禁用流行动画 - 或者默认弹出

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

现在,您可以创建MainActivity,并显示Fragment1,可以添加另一个Fragment2,然后可以依次弹出Fragment1

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

答案 8 :(得分:0)

在您想要在没有动画的情况下弹出的片段中覆盖它,并在输入

时保持动画
@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);
}

答案 9 :(得分:0)

更简单的解决方案:

for (fragment in supportFragmentManager.fragments) {
    removeFragment(fragment)
}
if (supportFragmentManager.backStackEntryCount > 0) {
    supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}

答案 10 :(得分:0)

现在,Android实际上有一种方法可以解决@Geoff周围的问题。

为避免动画在popBackStack()上运行,在给fragment充气时,请将.setReorderingAllowed(true)添加到fragmentTransaction

例如:

supportFragmentTransaction.beginTransaction()
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .setCustomAnimations(
        android.R.anim.fade_in,
        android.R.anim.fade_out,
        android.R.anim.fade_in,
        android.R.anim.fade_out
    )
    .replace(yourContainer.id, yourFragment)
    .commit()

您会注意到,如果设置setReorderingAllowed(true),则将不再播放弹出动画。结果实际上类似于@Geoff的答案。

答案 11 :(得分:-1)

回复杰夫和plackemacher评论。

您可以尝试从此片段中删除所有视图。然后片段将显示但它应该是透明的。

删除all-1(我使用导航抽屉,因此抽屉片段应保留)片段:

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

抱歉我的英文