片段事务动画滑入顶部

78
我正在尝试使用FragmentTransaction.setCustomAnimations实现以下效果。
  1. 显示Fragment A
  2. 用Fragment B替换Fragment A。在替换期间,Fragment A应该保持可见。Fragment B应从右侧滑入。在Fragment B滑入时,它应该覆盖在Fragment A上方。
我成功设置了滑入动画,但无法让Fragment A停留在原地并在滑入动画过程中被遮盖。无论我做什么,似乎Fragment A都在顶部。
如何实现这一点?
以下是FragmentTransaction的代码:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.nothing, R.anim.nothing,
    R.anim.slide_out_right);
ft.replace(R.id.fragment_content, fragment, name);
ft.addToBackStack(name);
ft.commit();

你可以看到,我为"out"动画定义了一个名为R.anim.nothing的动画,因为我实际上不希望片段A在事务期间做任何其他操作,只是保持原样。

这里是动画资源:

slide_in_right.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_mediumAnimTime"
    android:fromXDelta="100%p"
    android:toXDelta="0"
    android:zAdjustment="top" />

nothing.xml

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_mediumAnimTime"
    android:fromAlpha="1.0"
    android:toAlpha="1.0"
    android:zAdjustment="bottom" />

1
我已经提交了一个错误报告,因为当前的Z顺序是完全错误的。 https://code.google.com/p/android/issues/detail?id=163384&thanks=163384&ts=1428443402 - sbaar
12个回答

0

在布局中添加高程。

我在从底部滑入的片段中添加了30sp的高程,它起作用了。

我尝试了这里提出的许多解决方案。以下是结合所有想法和添加高程的完整代码和输出。

输出:

没有高程:

enter image description here

带有高程:

enter image description here

完整代码:

将高程添加到根目录

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:elevation="30sp">
     ----

     ----
 </RelativeLayout>

如何从底部滑入片段?

getSupportFragmentManager()
            .beginTransaction()
            .setCustomAnimations(R.anim.slide_in_bottom, R.anim.do_nothing, R.anim.do_nothing, R.anim.slide_out_bottom)
            .replace(R.id.fragmentContainer, currentFragment, "TAG")
            .addToBackStack("TAG")
            .commit();

当按下返回按钮时如何执行反向操作?

getSupportFragmentManager()
            .popBackStack();

由于我们已经在setCustomAnimations()方法中定义了进入和退出动画。调用popBackStack();会处理反向动画。

R.anim.slide_in_bottom

<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
       android:duration="500"
       android:fromYDelta="100%"
       android:toYDelta="0%">
    </translate>
</set>

R.anim.slide_out_bottom

<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="500"
        android:fromYDelta="0%"
        android:toYDelta="100%">
    </translate>
</set>

R.anim.do_nothing

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/linear_interpolator"
        android:fromXScale="1.0"
        android:toXScale="1.0"
        android:fromYScale="1.0"
        android:toYScale="1.0"
        android:duration="500"/>
     <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromAlpha="1.0"
        android:toAlpha="1.0"
        android:duration="500"
        android:startOffset="500" />
</set>

0
这是我目前的解决方法,供有兴趣的人参考。
在添加新Fragment的函数中:
final Fragment toRemove = fragmentManager.findFragmentById(containerID);
if (toRemove != null) {
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                fragmentManager.beginTransaction().hide(toRemove).commit();
            }
        }, 
        getResources().getInteger(android.R.integer.config_mediumAnimTime) + 100);
        // Use whatever duration you chose for your animation for this handler
        // I added an extra 100 ms because the first transaction wasn't always 
        // fast enough
}
fragmentManager.beginTransaction()
    .setCustomAnimations(enter, 0, 0, popExit).add(containerID, fragmentToAdd)
    .addToBackStack(tag).commit();

并且在onCreate中:

final FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                Fragment current = fragmentManager.findFragmentById(containerID);
                if (current != null && current.isHidden()) {
                    fragmentManager.beginTransaction().show(current).commit();
                }
            }
        });

我更喜欢使用AnimationListener而不是上面的Handler,但我没有看到任何方法可以使用它来检测事务动画的结束,除了像onCreateAnimation()这样与片段绑定的方式。如果有适当的监听器建议/编辑,将不胜感激。

我要指出的是,我以这种方式添加的Fragment都很轻量级,所以它们在片段容器中与它们所在的片段一起并存不会有问题。

如果您想要删除该片段,您可以在HandlerRunnable中放置fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss();,并在OnBackStackChangedListener中:

// Use back stack entry tag to get the fragment
Fragment current = getCurrentFragment(); 
if (current != null && !current.isAdded()) {
    fragmentManager.beginTransaction()
        .add(containerId, current, current.getTag())
        .commitNowAllowingStateLoss();
}

请注意,上面的解决方案不适用于容器中的第一个片段(因为它不在后退堆栈中),因此您必须有另一种方法来恢复该片段,也许是以某种方式保存对第一个片段的引用...但是,如果您不使用后退堆栈并始终手动替换片段,则这不是问题。或者,您可以将所有片段添加到后退堆栈中(包括第一个片段),并覆盖onBackPressed,以确保当后退堆栈中只剩下一个片段时,您的活动退出而不是显示空屏幕。
编辑:我发现以下函数可以取代上面的FragmentTransaction.remove()FragmentTransaction.add()函数:

FragmentTransaction.detach():

将给定的片段从 UI 中分离。这与将其放入后退栈时的状态相同:片段从 UI 中移除,但其状态仍由片段管理器积极管理。进入此状态时,其视图层次结构将被销毁。

FragmentTransaction.attach():

重新附加一个之前通过detach(Fragment)从UI中分离的片段。这将导致其视图层次结构被重新创建,附加到UI并显示出来。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接