片段事务动画:滑入和滑出

120

我查看了一些有关在片段之间进行动画转换的教程。我已经使用了这种方法来进行动画处理,它有效:

fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left,
                android.R.anim.slide_out_right);

但我想反转这个动画:旧碎片向左滑出,新碎片向右滑入,但是在我的范围内似乎没有任何 R.anim 文件的值是有用的。

我该怎么做?


在我看来,你可以尝试在第二个片段中override key_code==back_key_press - Nitin Misra
也许我没有表达清楚。我希望当片段交换时,旧片段从左侧滑出,新片段从右侧进入。使用该代码时,行为相反。 - giozh
1
在这里回答:https://dev59.com/1Ggv5IYBdhLWcg3wCsc8 - Carsten
7个回答

305

更新 针对 Android v19+,请参阅此链接 通过 @Sandra

您可以创建自己的动画。将动画 XML 文件放置在 res > anim

enter_from_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
  <translate 
      android:fromXDelta="-100%p" android:toXDelta="0%"
      android:fromYDelta="0%" android:toYDelta="0%"
      android:duration="@android:integer/config_mediumAnimTime"/>
</set>

从右侧进入.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
  <translate
     android:fromXDelta="100%p" android:toXDelta="0%"
     android:fromYDelta="0%" android:toYDelta="0%"
     android:duration="@android:integer/config_mediumAnimTime" />
</set>

exit_to_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
  <translate 
      android:fromXDelta="0%" android:toXDelta="-100%p"
      android:fromYDelta="0%" android:toYDelta="0%"
      android:duration="@android:integer/config_mediumAnimTime"/>
</set>

exit_to_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false">
  <translate
     android:fromXDelta="0%" android:toXDelta="100%p"
     android:fromYDelta="0%" android:toYDelta="0%"
     android:duration="@android:integer/config_mediumAnimTime" />
</set>

您可以将动画的持续时间更改为短时间

android:duration="@android:integer/config_shortAnimTime"

或漫长的动画时间

android:duration="@android:integer/config_longAnimTime" 

使用方法(注意在事务中调用方法的顺序很重要。在调用.replace 和 .commit 之前添加动画):


FragmentTransaction transaction = supportFragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
transaction.replace(R.id.content_frame, fragment);
transaction.addToBackStack(null);
transaction.commit();

30
当替换片段时,只需使用这些XML动画:fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right); fragmentTransaction.replace(R.id.content_frame, fragDettRisorsa); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); - moondroid
11
它说的是未知动画师的名字:未知动画师的名字 - Hirak Chhatbar
9
无法正常工作...引起了“java.lang.RuntimeException: Unknown animator name: translate”的错误。这个解决方案对我有用。http://trickyandroid.com/fragments-translate-animation/ - Ataru
27
在我看来,700 的动画持续时间有点长。Android框架为动画提供了3个预设的时间参数:android:duration="@android:integer/config_longAnimTime"android:duration="@android:integer/config_mediumAnimTime"android:duration="@android:integer/config_shortAnimTime",分别对应500、400和200。我猜测单位是毫秒,但不确定。 - Krøllebølle
6
仅在使用支持片段(android.support.v4.app.Fragment)时有效。 - Aviv Ben Shabat
显示剩余10条评论

50

在片段中,有三种方式来处理事务动画。

转场效果

如果需要使用内置的转场效果之一,请使用setTranstion()方法:

getSupportFragmentManager()
        .beginTransaction()
        .setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN )
        .show( m_topFragment )
        .commit()

自定义动画

您还可以使用setCustomAnimations()方法自定义动画:

getSupportFragmentManager()
        .beginTransaction()
        .setCustomAnimations( R.anim.slide_up, 0, 0, R.anim.slide_down)
        .show( m_topFragment )
        .commit()

slide_up.xml

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:propertyName="translationY"
        android:valueType="floatType"
        android:valueFrom="1280"
        android:valueTo="0"
        android:duration="@android:integer/config_mediumAnimTime"/>

slide_down.xml

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:propertyName="translationY"
        android:valueType="floatType"
        android:valueFrom="0"
        android:valueTo="1280"
        android:duration="@android:integer/config_mediumAnimTime"/>

多重动画

最后,我们也可以在一个事务中同时启动多个片段动画。这可以产生一个非常酷的效果,其中一个片段向上滑动,另一个片段向下滑动:

getSupportFragmentManager()
        .beginTransaction()
        .setCustomAnimations( R.anim.abc_slide_in_top, R.anim.abc_slide_out_top ) // Top Fragment Animation
        .show( m_topFragment )
        .setCustomAnimations( R.anim.abc_slide_in_bottom, R.anim.abc_slide_out_bottom ) // Bottom Fragment Animation
        .show( m_bottomFragment )
        .commit()

如需更详细信息,请访问此网址。

注意:您可以根据自己的需求检查动画,因为上述可能存在问题。


1
您正在使用绝对硬编码值。在更高分辨率的显示器上,前面的片段将会消失在屏幕中央。 - TheLibrarian
1
@TheLibrarianCz 这只是一个例子。 - duggu
2
这是也不是。 - TheLibrarian
1
这取决于某人是想要从上面的例子中获得“喂食”,还是想要学习他们可以通过例子实现什么。 - duggu
@DPrince 这是你的片段。 - duggu
显示剩余2条评论

11

向下滑入.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_longAnimTime"
        android:fromYDelta="0%p"
        android:toYDelta="100%p" />
</set>

slide_in_up.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_longAnimTime"
        android:fromYDelta="100%p"
        android:toYDelta="0%p" />
</set>

滑出下方.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_longAnimTime"
        android:fromYDelta="-100%"
        android:toYDelta="0"
        />
</set>

滑出向上.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_longAnimTime"
        android:fromYDelta="0%p"
        android:toYDelta="-100%p"
        />
</set>

方向 = 向下

            activity.getSupportFragmentManager()
                    .beginTransaction()
                    .setCustomAnimations(R.anim.slide_out_down, R.anim.slide_in_down)
                    .replace(R.id.container, new CardFrontFragment())
                    .commit();

方向 = 向上

           activity.getSupportFragmentManager()
                    .beginTransaction()
                    .setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up)
                    .replace(R.id.container, new CardFrontFragment())
                    .commit();

7
我有同样的问题,我使用了简单的解决方案。
1)在anim文件夹中创建sliding_out_right.xml。
  <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:fromXDelta="0" android:toXDelta="-50%p"
            android:duration="@android:integer/config_mediumAnimTime"/>
        <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
            android:duration="@android:integer/config_mediumAnimTime" />
    </set>

2) 在 anim 文件夹中创建 sliding_in_left.xml。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="50%p" android:toXDelta="0"
        android:duration="@android:integer/config_mediumAnimTime"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
        android:duration="@android:integer/config_mediumAnimTime" />
</set>

"3) 只需使用片段事务 setCustomeAnimations(),其中包括两个自定义 XML 和两个默认 XML 的动画,如下所示:-"
 fragmentTransaction.setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right );

2
这是我使用的另一种解决方案:
public class CustomAnimator {
    private static final String TAG = "com.example.CustomAnimator";

    private static Stack<AnimationEntry> animation_stack    = new Stack<>();

    public static final int                 DIRECTION_LEFT  = 1;
    public static final int                 DIRECTION_RIGHT = -1;
    public static final int                 DIRECTION_UP    = 2;
    public static final int                 DIRECTION_DOWN  = -2;

    static class AnimationEntry {
        View in;
        View    out;
        int     direction;
        long    duration;
    }

    public static boolean hasHistory() {
        return !animation_stack.empty();
    }

    public static void reversePrevious() {
        if (!animation_stack.empty()) {
            AnimationEntry entry = animation_stack.pop();
            slide(entry.out, entry.in, -entry.direction, entry.duration, false);
        }
    }

    public static void clearHistory() {
        animation_stack.clear();
    }

    public static void slide(final View in, View out, final int direction, long duration) {
        slide(in, out, direction, duration, true);
    }

    private static void slide(final View in, final View out, final int direction, final long duration, final boolean save) {

        ViewGroup in_parent = (ViewGroup) in.getParent();
        ViewGroup out_parent = (ViewGroup) out.getParent();

        if (!in_parent.equals(out_parent)) {
            return;
        }

        int parent_width = in_parent.getWidth();
        int parent_height = in_parent.getHeight();

        ObjectAnimator slide_out;
        ObjectAnimator slide_in;

        switch (direction) {
            case DIRECTION_LEFT:
            default:
                slide_in = ObjectAnimator.ofFloat(in, "translationX", parent_width, 0);
                slide_out = ObjectAnimator.ofFloat(out, "translationX", 0, -out.getWidth());
                break;
            case DIRECTION_RIGHT:
                slide_in = ObjectAnimator.ofFloat(in, "translationX", -out.getWidth(), 0);
                slide_out = ObjectAnimator.ofFloat(out, "translationX", 0, parent_width);
                break;
            case DIRECTION_UP:
                slide_in = ObjectAnimator.ofFloat(in, "translationY", parent_height, 0);
                slide_out = ObjectAnimator.ofFloat(out, "translationY", 0, -out.getHeight());
                break;
            case DIRECTION_DOWN:
                slide_in = ObjectAnimator.ofFloat(in, "translationY", -out.getHeight(), 0);
                slide_out = ObjectAnimator.ofFloat(out, "translationY", 0, parent_height);
                break;
        }

        AnimatorSet animations = new AnimatorSet();
        animations.setDuration(duration);
        animations.playTogether(slide_in, slide_out);
        animations.addListener(new Animator.AnimatorListener() {

            @Override
            public void onAnimationCancel(Animator arg0) {
            }

            @Override
            public void onAnimationEnd(Animator arg0) {
                out.setVisibility(View.INVISIBLE);
                if (save) {
                    AnimationEntry ae = new AnimationEntry();
                    ae.in = in;
                    ae.out = out;
                    ae.direction = direction;
                    ae.duration = duration;
                    animation_stack.push(ae);
                }
            }

            @Override
            public void onAnimationRepeat(Animator arg0) {
            }

            @Override
            public void onAnimationStart(Animator arg0) {
                in.setVisibility(View.VISIBLE);
            }
        });
        animations.start();
    }
}

类的用法。假设您有两个片段(列表片段和详细信息片段),如下所示:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ui_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@+id/list_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/details_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />
</FrameLayout>

使用方法

View details_container = findViewById(R.id.details_container);
View list_container = findViewById(R.id.list_container);
// You can select the direction left/right/up/down and the duration
CustomAnimator.slide(list_container, details_container,CustomAnimator.DIRECTION_LEFT, 400);

您可以使用函数CustomAnimator.reversePrevious();在用户按下返回按钮时获取先前的视图。

1
你确定你正在使用Android的Fragments吗?=)看起来像是使用自定义的片段管理基于视图的系统。 - kissed

0

在从一个片段转换到另一个片段的过程中,遇到了白屏问题。在navigation.xml中设置了导航和动画。

enter image description here

所有片段的背景都相同,但是显示为白色空白屏幕。因此,在执行转换期间,我在片段中设置了navOptions。

          //Transition options
        val options = navOptions {
            anim {
                enter = R.anim.slide_in_right
                exit = R.anim.slide_out_left
                popEnter = R.anim.slide_in_left
                popExit = R.anim.slide_out_right
            }
        }

.......................

  this.findNavController().navigate(SampleFragmentDirections.actionSampleFragmentToChartFragment(it),
                    options)

这对我有用。在转换之间没有白屏。太神奇了)


0

对我来说,唯一能够消除动画白色背景的方法是将Fragment更改为DialogFragment,并添加以下代码:

创建了以下样式:

<style name="WindowSlideInAndOutAnimation">
        <item name="android:windowEnterAnimation">@anim/slide_in_bottom</item>
        <item name="android:windowExitAnimation">@anim/slide_out_top</item>
    </style>

    <style name="AppFullScreenDialogTransparentTitleBar" parent="Theme.MaterialComponents.Light.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">false</item>
        <item name="colorPrimaryDark">@android:color/transparent</item>
    </style>

然后在对话框片段中引用这些样式:

override fun getTheme() = R.style.AppFullScreenDialogTransparentTitleBar

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        dialog?.window?.setWindowAnimations(R.style.WindowSlideInAndOutAnimation)

//rest of code on view created here
}

然后我只需通过导航组件正常导航到片段(还要注意更改导航 XML 文件中片段的类型为对话框)


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