本文基于Android 13

通过xml引入动画

适用于应用内有多个Activity的场景,这些Activity都在同一个Task任务栈内, 换而言之,下面的配置对Task之间的切换动画不生效,launchMode也会有影响,详细说明请参考 不同Android版本之间的区别

定义动画文件

在res/anim下定义两个动画文件

slide_in_left.xml

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

slide_out_right.xml

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

在style中指定动画

然后定义animation-themes.xml,引用上边定义的动画资源,定义style name = windowAnimationStyle,这个style中的动画字段也可以直接写入themes.xml中,省略一个文件, parent指定为:android:Animation.Activity

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Window animation style -->
    <style name="windowAnimationStyle" parent="android:Animation.Activity">
        <item name="android:activityOpenEnterAnimation">@anim/slide_in_left</item>
        <item name="android:activityOpenExitAnimation">@anim/slide_out_right</item>
    </style>
</resources>

配置主题

values/themes.xml主题中引用动画, parent指定为Theme.AppCompat或其子类, 引用动画关键属性: <item name="android:windowAnimationStyle">@style/windowAnimationStyle</item>

<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Theme.Demo.WindowAnimation" parent="@style/Theme.AppCompat" >
        <item name="android:windowEnableSplitTouch">false</item>
        <item name="android:splitMotionEvents">false</item>
        // 窗口动画
        <item name="android:windowAnimationStyle">@style/windowAnimationStyle</item>
        <item name="android:windowBackground">@color/transparent</item>
    </style>
    <style name="Theme.Demo.NoAnimation" parent="@style/Theme.AppCompat" >
        <item name="android:windowEnableSplitTouch">false</item>
        <item name="android:splitMotionEvents">false</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowBackground">@color/transparent</item>
    </style>
</resources>

应用主题

最后在AndroidManifext.xml引入主题

应用到application

在application中添加 android:theme="@style/Theme.Demo"

<application
    ...
    android:theme="@style/Theme.Demo.WindowAnimation"
    <activity
        android:name=".MainActivity"
        ...

启动app后,在内部跳转activity,可以看到自定义的窗口过度动画

应用到指定Activity

<application
    ...
    android:theme="@style/Theme.Demo.NoAnimation"
    ...
    <activity
        android:name=".CustomAnimationActivity"
        android:theme="@style/Theme.Demo.WindowAnimation"
        ...

不同Android版本之间的区别

上边的动画属性还包括任务栈之间的切换动画

        <item name="android:activityOpenEnterAnimation">@anim/slide_in_from_bottom</item>
        <item name="android:activityCloseExitAnimation">@anim/slide_out_from_bottom</item>

        <item name="android:taskOpenEnterAnimation">@anim/slide_in_from_bottom</item>
        <item name="android:taskOpenExitAnimation">@anim/slide_out_from_bottom</item>

        <item name="android:taskCloseExitAnimation">@anim/slide_out_from_bottom</item>
        <item name="android:taskCloseEnterAnimation">@anim/slide_in_from_bottom</item>

        <item name="android:taskToFrontEnterAnimation">@anim/slide_in_from_bottom</item>
        <item name="android:taskToFrontExitAnimation">@anim/slide_out_from_bottom</item>

        <item name="android:taskToBackExitAnimation">@anim/slide_out_from_bottom</item>
        <item name="android:taskToBackEnterAnimation">@anim/slide_in_from_bottom</item>

任务栈切换动画 taskOpenEnterAnimation 在Android R及以上版本失效,在Android Q上 通过back事件进后台后切换到前台失效,通过home事件进后台再切到前台有效,这里的切换到前台指的是通过adb shell am start -a 的方式,如果是通过点击launcher图标来启动的话,只有在Android O上有效

通过代码添加动画

动态设置主题

代码应用到Activity

super.onCreate之前添加一行 setTheme(R.style.Theme_windowAnimation)

override fun onCreate(savedInstanceState: Bundle?) {
    setTheme(R.style.Theme_windowAnimation)
    super.onCreate()
    ...
}

在Activity内部设置过度动画

同样,首先在res/anim/下配置好in和out的动画资源,在activity中,setContentView之前调用 overridePendingTransition 指定当前activity的动画资源

    override fun onCreate(savedInstanceState: Bundle?) {
        overridePendingTransition(R.anim.slide_in_from_bottom,R.anim.slide_out_from_bottom)
        super.onCreate(savedInstanceState)
    }

在Android aosp模拟器测试过

从Android S开始 ,此函数只能为Task任务栈里边的activity添加过度动画,Task任务栈之间的动画切换只能使用系统默认动画,函数定义:

    /**
     * Call immediately after one of the flavors of {@link #startActivity(Intent)}
     * or {@link #finish} to specify an explicit transition animation to
     * perform next.
     *
     * <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN} an alternative
     * to using this with starting activities is to supply the desired animation
     * information through a {@link ActivityOptions} bundle to
     * {@link #startActivity(Intent, Bundle)} or a related function.  This allows
     * you to specify a custom animation even when starting an activity from
     * outside the context of the current top activity.
     *
     * <p>Af of {@link android.os.Build.VERSION_CODES#S} application can only specify
     * a transition animation when the transition happens within the same task. System
     * default animation is used for cross-task transition animations.
     *
     * @param enterAnim A resource ID of the animation resource to use for
     * the incoming activity.  Use 0 for no animation.
     * @param exitAnim A resource ID of the animation resource to use for
     * the outgoing activity.  Use 0 for no animation.
     */
    public void overridePendingTransition(int enterAnim, int exitAnim) {
        overridePendingTransition(enterAnim, exitAnim, 0);
    }

对于Android 14

新增了一个接口overrideType 可选 OVERRIDE_TRANSITION_OPEN,OVERRIDE_TRANSITION_CLOSE

    public void overrideActivityTransition(int overrideType, int enterAnim, int exitAnim, int backgroundColor) {
        
    }

启动Activity时设置动画

  1. startActivity之后可以紧跟一个overridePendingTransition来设置动画

  2. startActivity传入ActivityOptions参数

val activityOptions = ActivityOptions.makeCustomAnimation(context, R.anim.slide_in, 0 )
context.startActivity(intent, activityOptions.toBundle())

一些动画不生效的场景

  • 主题引用错误,引用主题的时候直接引用 R.style.windowAnimationStyle,应该引用R.style.Theme.Demo,WindowAnimation

  • Android 版本限制,比如overridePendingTransition 在Android < S,对Task切换动画生效,高版本只对同一个Task内的Activity生效

  • 受到启动模式影响,singleInstance模式下动画不会生效,因为每个Activity都位于不同的Task

  • 在高版本中Android 已经不支持应用定义Task之间切换的动画

春风花气馥,秋月寒江湛