启动画面

从 Android 12 开始,SplashScreen API 允许应用以动画形式启动,包括启动时的应用内运动、显示应用图标的启动画面以及过渡到应用本身。SplashScreen 是一个 Window,因此会覆盖一个 Activity

图 1. 启动画面。

启动画面体验为每个应用启动带来了标准设计元素,但它也是可定制的,以便您的应用能够保持其独特的品牌。

除了使用 SplashScreen 平台 API 外,您还可以使用包裹 SplashScreen API 的 SplashScreen compat 库。

启动画面的工作原理

当用户启动应用时,如果应用的进程未运行(冷启动)或 Activity 未创建(温启动),则会发生以下事件:

  1. 系统使用主题和您定义的任何动画显示启动画面。

  2. 当应用准备就绪时,启动画面会消失,然后应用显示。

启动画面在热启动期间永远不会显示。

启动画面的元素和机制

启动画面的元素在 Android 清单文件中的 XML 资源文件中定义。每个元素都有浅色和深色模式版本。

启动画面的可定制元素包括应用图标、图标背景和窗口背景:

An image showing the elements contained in a splash screen
图 2. 启动画面的可定制元素。

请考虑图 2 中所示的以下元素:

1 应用图标必须是矢量可绘制对象。它可以是静态的或动态的。虽然动画持续时间不限,但我们建议不要超过 1000 毫秒。启动器图标是默认图标。

2 图标背景是可选的,如果您需要在图标和窗口背景之间增加对比度,它会非常有用。如果您使用自适应图标,并且其背景与窗口背景有足够的对比度,则会显示其背景。

3 与自适应图标一样,前景的三分之一会被遮罩。

4 窗口背景由单一的不透明颜色组成。如果窗口背景已设置且是纯色,则在未设置属性时默认使用该颜色。

启动画面尺寸

启动画面图标使用与自适应图标相同的规范,具体如下:

  • 品牌图片:必须为 200×80 dp。
  • 带有图标背景的应用图标:必须为 240×240 dp,并适合直径为 160 dp 的圆。
  • 不带图标背景的应用图标:必须为 288×288 dp,并适合直径为 192 dp 的圆。

例如,如果图片的完整尺寸为 300×300 dp,则图标需要适合直径为 200 dp 的圆。圆外的一切内容都将变为不可见(被遮罩)。

An image showing different icon dimensions for solid and transparent background
图 3. 启动画面图标尺寸,分别用于纯色和透明背景。

启动画面动画和启动序列

在冷启动时启动应用通常会伴随额外的延迟。在启动画面中添加动画图标具有明显的审美吸引力,并提供更高级的体验。用户研究表明,观看动画时,感知的启动时间会缩短。

启动画面动画嵌入在启动序列组件中,如图 4 所示。

An image showing the launch sequence in twelve consecutive frames, beginning with the launcher icon being tapped and filling the screen as it enlarges
图 4. 启动序列。
  1. 进入动画:这包括系统视图到启动画面。它由系统控制,不可定制。

  2. 启动画面(在序列的“等待”部分显示):启动画面是可定制的,允许您提供自己的徽标动画和品牌。它必须符合本页面要求中描述的规范才能正常工作。

  3. 退出动画:这包括隐藏启动画面的动画。如果您想自定义它,请使用 SplashScreenView 及其图标。您可以对它们运行任何动画,并设置变换、不透明度和颜色。在这种情况下,当动画完成时手动移除启动画面。

运行图标动画时,如果应用提前准备就绪,应用启动时可以选择跳过序列。应用会触发 onResume() 或启动画面自动超时,因此请确保动画可以轻松跳过。启动画面必须仅在应用从视觉角度稳定时(即不需要额外的加载图标时)通过 onResume() 移除。显示不完整的界面可能会让用户感到突兀,并可能给人留下不可预测或不够完善的印象。

启动画面动画要求

您的启动画面必须符合以下规范:

  • 设置单一窗口背景颜色,不透明。日间和夜间模式支持通过 SplashScreen compat 库实现。

  • 确保动画图标符合以下规范:

    • 格式:图标必须是 AnimatedVectorDrawable (AVD) XML。
    • 尺寸:AVD 图标的大小必须是自适应图标的四倍,具体如下:
      • 图标区域必须为 432 dp,换句话说,是未遮罩自适应图标 108 dp 区域的四倍。
      • 图片内侧三分之二在启动器图标上可见,必须为 288 dp,换句话说,是自适应图标内侧遮罩区域 72 dp 的四倍。
    • 持续时间:我们建议在手机上不要超过 1000 毫秒。您可以使用延迟启动,但这不能超过 166 毫秒。如果应用启动时间超过 1000 毫秒,请考虑使用循环动画。
  • 确定何时关闭启动画面的适当时间,这发生在您的应用绘制第一帧时。您可以按照让启动画面在屏幕上停留更长时间部分所述进一步自定义此设置。

启动画面资源

图 5. AVD 示例。

下载示例启动工具包,其中演示了如何创建、格式化动画并将其导出为 AVD。它包括以下内容:

  • 动画的 Adobe After Effects 项目文件。
  • 最终导出的 AVD XML 文件。
  • 动画的 GIF 示例。

下载这些文件即表示您同意 Google 服务条款

Google 隐私权政策描述了此服务中数据的处理方式。

在应用中自定义启动画面

默认情况下,如果主题的 windowBackground 是单色,SplashScreen 会使用该主题的 windowBackground。要自定义启动画面,请在应用主题中添加属性。

您可以通过以下任何一种方式自定义应用的启动画面:

  • 设置主题属性来更改其外观。

  • 让它在屏幕上停留更长时间。

  • 自定义关闭启动画面的动画。

入门

核心 SplashScreen 库将 Android 12 启动画面带到 API 23 及以上的所有设备。要将其添加到您的项目中,请将以下代码段添加到您的 build.gradle 文件中:

Groovy

dependencies {
    implementation "androidx.core:core-splashscreen:1.0.0"
}

Kotlin

dependencies {
    implementation("androidx.core:core-splashscreen:1.0.0")
}

设置启动画面的主题以更改其外观

您可以在 Activity 主题中指定以下属性来为您的应用自定义启动画面。如果您已经有一个使用 android:windowBackground 等属性的旧版启动画面实现,请考虑为 Android 12 及更高版本提供备用资源文件。

  1. 使用 windowSplashScreenBackground 用特定的单色填充背景。

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. 使用 windowSplashScreenAnimatedIcon 替换起始窗口中心的图标。

    仅适用于以 Android 12(API 级别 32)为目标平台的应用,请执行以下操作:

    如果对象可通过 AnimationDrawableAnimatedVectorDrawable 进行动画和绘制,请设置 windowSplashScreenAnimationDuration 以在显示起始窗口时播放动画。Android 13 不需要这样做,因为持续时间直接从 AnimatedVectorDrawable 推断。

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. 使用 windowSplashScreenAnimationDuration 指示启动画面图标动画的持续时间。设置此属性不会对启动画面的实际显示时间产生任何影响,但在使用 SplashScreenView.getIconAnimationDuration 自定义启动画面退出动画时,您可以检索它。有关详细信息,请参阅关于让启动画面在屏幕上停留更长时间的部分。

    <item name="android:windowSplashScreenAnimationDuration">1000</item>
    
  4. 使用 windowSplashScreenIconBackgroundColor 设置启动画面图标后面的背景。如果窗口背景和图标之间的对比度不足,这将很有用。

    <item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>
    
  5. 您可以使用 windowSplashScreenBrandingImage 设置要在启动画面底部显示的图像。但是,设计指南不建议使用品牌图像。

    <item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
    
  6. 您可以使用 windowSplashScreenBehavior 指定您的应用在 Android 13 及更高版本中是否始终在启动画面上显示图标。默认值为 0,如果启动 Activity 将 splashScreenStyle 设置为 SPLASH_SCREEN_STYLE_ICON,则在启动画面上显示图标,如果启动 Activity 未指定样式,则遵循系统行为。如果您希望从不显示空启动画面,并且始终希望显示动画图标,请将其设置为值 icon_preferred

    <item name="android:windowSplashScreenBehavior">icon_preferred</item>
    

让启动画面在屏幕上停留更长时间

启动画面在您的应用绘制第一帧后立即消失。如果您需要加载少量数据(例如从本地磁盘异步加载应用内设置),可以使用 ViewTreeObserver.OnPreDrawListener 暂停应用绘制第一帧。

如果您的起始 Activity 在绘制完成之前就结束了(例如,通过不设置内容视图并在 onResume 之前完成),则不需要 pre-draw listener。

Kotlin

// Create a new event for the activity.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Set the layout for the content view.
    setContentView(R.layout.main_activity)

    // Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Check whether the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready. Start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content isn't ready. Suspend.
                    false
                }
            }
        }
    )
}

Java

// Create a new event for the activity.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the layout for the content view.
    setContentView(R.layout.main_activity);

    // Set up an OnPreDrawListener to the root view.
    final View content = findViewById(android.R.id.content);
    content.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Check whether the initial data is ready.
                    if (mViewModel.isReady()) {
                        // The content is ready. Start drawing.
                        content.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    } else {
                        // The content isn't ready. Suspend.
                        return false;
                    }
                }
            });
}

自定义关闭启动画面的动画

您可以通过 Activity.getSplashScreen() 进一步自定义启动画面的动画。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...

    // Add a callback that's called when the splash screen is animating to the
    // app content.
    splashScreen.setOnExitAnimationListener { splashScreenView ->
        // Create your custom animation.
        val slideUp = ObjectAnimator.ofFloat(
            splashScreenView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.doOnEnd { splashScreenView.remove() }

        // Run your animation.
        slideUp.start()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    // Add a callback that's called when the splash screen is animating to the
    // app content.
    getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                splashScreenView.remove();
            }
        });

        // Run your animation.
        slideUp.start();
    });
}

在此回调开始时,启动画面上的 animated vector drawable 会开始播放。根据应用启动的持续时间,可绘制对象可能正在动画过程中。使用 SplashScreenView.getIconAnimationStart 了解动画何时开始。您可以通过以下方式计算图标动画的剩余持续时间:

Kotlin

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDuration
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationStart
// Calculate the remaining duration of the animation.
val remainingDuration = if (animationDuration != null && animationStart != null) {
    (animationDuration - Duration.between(animationStart, Instant.now()))
        .toMillis()
        .coerceAtLeast(0L)
} else {
    0L
}

Java

// Get the duration of the animated vector drawable.
Duration animationDuration = splashScreenView.getIconAnimationDuration();
// Get the start time of the animation.
Instant animationStart = splashScreenView.getIconAnimationStart();
// Calculate the remaining duration of the animation.
long remainingDuration;
if (animationDuration != null && animationStart != null) {
    remainingDuration = animationDuration.minus(
            Duration.between(animationStart, Instant.now())
    ).toMillis();
    remainingDuration = Math.max(remainingDuration, 0L);
} else {
    remainingDuration = 0L;
}

其他资源