启动画面

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

图 1. 启动画面。

启动画面体验为每个应用启动带来了标准设计元素,但它也是可自定义的,因此您的应用可以保持其独特的品牌形象。

除了使用 SplashScreen 平台 API 之外,您还可以使用 SplashScreen 兼容库,该库封装了 SplashScreen API。

启动画面的工作原理

当用户在应用进程未运行时(冷启动)或 Activity 未创建(暖启动)启动应用时,会发生以下事件

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

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

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

启动画面的元素和机制

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

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

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

考虑以下元素,如图 2 所示

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

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() 消失,因此不需要额外的微调器。引入不完整的界面可能会让用户感到不适,并可能给人留下不可预测或缺乏润色的印象。

启动画面动画要求

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

  • 使用不透明度为 0 的单个窗口背景颜色设置背景颜色。日间模式和夜间模式受 SplashScreen 兼容库 支持。

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

    • 格式: 图标必须是 AnimatedVectorDrawable (AVD) XML。
    • 尺寸: AVD 图标必须是自适应图标尺寸的四倍,如下所示
      • 图标区域必须为 432 dp,即未遮罩自适应图标区域的 108 dp 的四倍。
      • 图像的内三分之二在启动器图标上可见,必须为 288 dp,即构成自适应图标内部遮罩区域的 72 dp 的四倍。
    • 持续时间: 我们建议在手机上不要超过 1,000 毫秒。您可以使用延迟启动,但这不能超过 166 毫秒。如果应用启动时间超过 1,000 毫秒,请考虑使用循环动画。
  • 确定一个合适的启动画面消失时间,这将发生在您的应用绘制其第一帧时。您可以按照 将启动画面保持在屏幕上更长时间 部分中所述进一步自定义它。

启动画面资源

图 5. AVD 示例。

下载 示例入门工具包,它演示了如何创建、格式化并将动画导出到 AVD。它包括以下内容

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

通过下载这些文件,您同意 Google 服务条款

Google 隐私政策 描述了在本服务中如何处理数据。

在您的应用中自定义启动画面

默认情况下,SplashScreen 使用主题的 windowBackground(如果 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 之前结束),则不需要预绘制侦听器。

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();
    });
}

在此回调开始时,启动画面上的 动画矢量可绘制对象 将开始。根据应用启动的持续时间,可绘制对象可能处于动画的中间阶段。使用 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;
}

其他资源