Android修炼系列(十七),Activity过渡动画指南

6,069 阅读3分钟

我们可以为Activity的进入和退出过度设置动画,以及Activity之间过渡时指定共享元素的自定义动画,其中:

  • 进入 - 是控制Activity中的视图如何进入场景。例如,通过“爆炸式”的效果让视图从外场进入并飞向屏幕中心。

  • 退出 - 是控制Activity中的视图如何退出场景。例如,通过“爆炸式”的效果让视图从屏幕中心离开。

  • 共享元素 - 是控制两个Activity共享的视图如何在这些Activity之间过渡。例如,如果两个Activity使用相同的图片(但位置和大小不同),Activity之间进行过渡时,changeImageTransform就能使共享元素在过度过程中流畅地平移和缩放图片。

Android 支持以下进入和退出过渡:

  • 爆炸式 - 将视图移入场景中心或从中移出。
  • 滑动式 - 将视图从场景的其中一个边缘移入或移出。
  • 淡入淡出式 - 通过更改视图的不透明度,在场景中添加视图或从中移除视图。

Android 还支持以下共享元素过渡:

  • changeBounds - 为目标视图布局边界的变化添加动画效果。
  • changeClipBounds - 为目标视图裁剪边界的变化添加动画效果。
  • changeTransform - 为目标视图缩放和旋转方面的变化添加动画效果。
  • changeImageTransform - 为目标图片尺寸和缩放方面的变化添加动画效果。

下面以进入过度-爆炸式为例,我们来看下效果:

2021-05-30 at 00.19.37.gif

进入过度实现

  1. 我们启用窗口内容过渡,注意Activity过渡API仅支持Android 5.0 (API 21)及更高版本。我们可以在Activity#onCreate 中直接代码设置:
    /* ActivityB.java */
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // 启用窗口内容过度
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        // 设置进入过度效果-爆炸式
        getWindow().setEnterTransition(new Explode());
        // 从当前Activity返回上个Activity时,采用默认过度效果
        getWindow().setReturnTransition(new AutoTransition());
    }

当然我们也可以直接在values-v21/styles.xml中设置:

    <item name="android:windowActivityTransitions">true</item>

    <!-- specify enter and exit transitions -->
    <item name="android:windowEnterTransition">@transition/explode_transition</item>
    <item name="android:windowExitTransition">@transition/explode</item>

其中res/transition/explode_transition.xml见下:

    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
        <explode/>
    </transitionSet>

其中res/transition/auto_transition.xml见下:

    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
        <autoTransition/>
    </transitionSet>
  1. 启动使用过渡的Activity,在此例中,我们从ActivityA->ActivityB,有代码:
    /* ActivityA.java */
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
    }

退出过度实现

退出过渡实现,与进入过度的实现一样,就不重复写了,退出-爆炸式的方法如下:

    getWindow().setExitTransition(new Explode());

这是ActivityA->ActivityB时,ActivityA的退出过渡效果:

2021-05-30 at 02.36.10.gif

共享元素过度实现

我们要在含有共享元素的两个Activity之间添加屏幕过渡动画,一般需要如下操作:

  1. 在主题背景中启用窗口内容过渡。

  2. 在样式中指定共享元素过渡效果。

  3. 定义相应过渡效果的XML资源。

  4. 使用 android:transitionName 属性为两个布局中的共享元素指定一个通用名称。

  5. 调用 ActivityOptions.makeSceneTransitionAnimation() 函数。

我们先来看一个共享元素过渡的效果:

2021-05-30 at 01.00.47.gif

我们先在values-v21/styles.xml中,启用窗口内容过渡,并指定共享元素过渡效果:

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        ...
        <item name="android:windowActivityTransitions">true</item>
        
        <item name="android:windowSharedElementEnterTransition">
            @transition/change_bounds_image_transition
        </item>
        <item name="android:windowSharedElementExitTransition">
            @transition/change_bounds_image_transition
        </item>
    </style>

其次我们定义change_bounds_image_transition.xml:

    <!-- res/transition/change_bounds_image_transition.xml -->
    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
        <changeBounds />
        <changeImageTransform />
    </transitionSet>

随后我们使用android:transitionName属性为两个布局中的共享元素指定一个通用名称,在ActivityA中如下:

    /* ActivityA.java , 并为其 layout中的imageview和textview 设置一个名称 */
    @SuppressWarnings("unchecked")
    ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
            IndexActivity.this,

            /* imageview_item : "detail:header:image" */
            new Pair<>(view.findViewById(R.id.imageview_item),
                            DetailActivity.VIEW_NAME_HEADER_IMAGE),
            /* textview_name : "detail:header:title" */
            new Pair<>(view.findViewById(R.id.textview_name),
                            DetailActivity.VIEW_NAME_HEADER_TITLE));
   

我们再为ActivityB内的共享元素指定相同名称:

    /* ActivityB.java , 指定共享元素名称,与ActivityA一致 */
    mHeaderImageView = findViewById(R.id.imageview_header);
    mHeaderTitle = findViewById(R.id.textview_title);

    /* mHeaderImageView : "detail:header:image" */
    ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
    /* mHeaderTitle : "detail:header:title" */
    ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);

启动跳转ActivityA->ActivityB即可:

    ActivityCompat.startActivity(IndexActivity.this, intent, activityOptions.toBundle());

note

在代码完成后,发现上面的共享元素效果,不设置transition也没有影响,这是为何呢?获取了下Transition,发现有数据:

    final Transition transition = getWindow().getSharedElementEnterTransition();
    // transition.mTranstions size = 4 : 
    // 1.ChangeBounds@9784d13
    // 2.ChangeTransform@f66cb50
    // 3.ChangeClipBounds@972149
    // 4.ChangeImageTransform@3f39a4e

这应该是在源码内android.R.styleable#Window_windowSharedElementEnterTransition有默认实现了吧。

代码我都放在github上了,如想对于过渡动画有个更具体的了解,也可查看google developer 官网。下节我会再介绍下Lottie动画库是如何实现的,敬请期待吧。

本文到这里就结束了,希望对你有帮助。