Android 12 添加了 SplashScreen
API,它可为所有应用启用新的应用启动动画。这包括启动时的进入应用运动、显示应用图标的启动画面,以及向应用本身的过渡。

这种新体验可让应用每次启动时都呈现标准设计元素,但它也可自定义,以便您的应用能够保持其独特的品牌。
启动画面的工作原理
当用户启动应用而应用的进程未在运行(冷启动)或 Activity 尚未创建(温启动)时,会发生以下事件。(在热启动期间从不显示启动画面。)
系统使用主题以及您已定义的任何动画显示启动画面。
当应用准备就绪时,会关闭启动画面并显示应用。
动画的元素和机制
动画的元素由 Android 清单中的 XML 资源文件定义。每个元素都有浅色模式和深色模式版本。
它们由窗口背景、动画形式的应用图标和图标背景组成:

关于这些元素,请注意以下几点:
应用图标 (1) 应该是矢量可绘制对象,它可以是静态或动画形式。虽然动画的时长可以不受限制,但我们建议让其不超过 1000 毫秒。默认情况下,使用启动器图标。
图标背景 (2) 是可选的,在图标与窗口背景之间需要更高的对比度时很有用。如果您使用一个自适应图标,当该图标与窗口背景之间的对比度足够高时,就会显示其背景。
与自适应图标一样,前景的 ⅓ 被遮盖 (3)。
窗口背景 (4) 由不透明的单色组成。如果窗口背景已设置且为纯色,则未设置相应的属性时默认使用该背景。
启动画面动画机制由进入动画和退出动画组成。
进入动画由系统视图到启动画面组成。这由系统控制且不可自定义。
退出动画由隐藏启动画面的动画运行组成。如果您要对其进行自定义,您将可以访问
SplashScreenView
及其图标,并且可以在它们之上运行任何动画(需要设置转换、不透明度和颜色)。在这种情况下,当动画完成时,需要手动移除启动画面。
自定义应用中的启动画面
默认情况下,SplashScreen
使用主题的 windowBackground
(如果它是单色)和启动器图标。启动画面的自定义通过向应用主题添加属性来完成。
您可以通过以下任一方式自定义应用的启动画面:
设置主题属性以更改其外观
让其在屏幕上显示更长时间
自定义用于关闭启动画面的动画
设置启动画面的主题以更改其外观
您可以在 Activity 主题中指定以下属性来自定义应用的启动画面。如果您已有使用 android:windowBackground
等属性的旧版启动画面实现,不妨考虑为 Android 12 提供替代资源文件。
使用
windowSplashScreenBackground
以特定的单色填充背景:<item name="android:windowSplashScreenBackground">@color/...</item>
使用
windowSplashScreenAnimatedIcon
替换起始窗口中心的图标。如果对象通过AnimationDrawable
和AnimatedVectorDrawable
可呈现动画效果且可绘制,则也会在显示起始窗口的同时播放动画。<item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
使用
windowSplashScreenAnimationDuration
设置启动画面在关闭之前显示的时长。最长时间为 1000 毫秒。使用
windowSplashScreenIconBackground
设置启动画面图标后面的背景。当窗口背景与图标之间的对比度不够高时,这很有用。<item name=”android:windowSplashScreenIconBackground”>@color/...</item>
(可选)您可以使用
windowSplashScreenBrandingImage
设置要显示在启动画面底部的图片。设计准则建议不要使用品牌图片。<item name=”android:windowSplashScreenBrandingImage”>@drawable/...</item>
让启动画面在屏幕上显示更长时间
当应用绘制第一帧后,启动画面会立即关闭。如果您需要从本地磁盘异步加载少量数据(如应用内主题设置),您可以使用 ViewTreeObserver.OnPreDrawListener
让应用暂停绘制第一帧。
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 if the initial data is ready. return if (viewModel.isReady) { // The content is ready; start drawing. content.viewTreeObserver.removeOnPreDrawListener(this) true } else { // The content is not 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 if the initial data is ready. if (mViewModel.isReady()) { // The content is ready; start drawing. content.getViewTreeObserver().removeOnPreDrawListener(this); return true; } else { // The content is not 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.getIconAnimationStartMillis
可了解动画何时开始。您可以按如下方式计算图标动画的剩余时长:
Kotlin
// Get the duration of the animated vector drawable. val animationDuration = splashScreenView.iconAnimationDurationMillis // Get the start time of the animation. val animationStart = splashScreenView.iconAnimationDurationMillis // Calculate the remaining duration of the animation. val remainingDuration = ( animationDuration - (SystemClock.uptimeMillis() - animationStart) ).coerceAtLeast(0L)
Java
// Get the duration of the animated vector drawable. long animationDuration = splashScreenView.getIconAnimationDurationMillis(); // Get the start time of the animation. long animationStart = splashScreenView.getIconAnimationStartMillis(); // Calculate the remaining duration of the animation. long remainingDuration = Math.max( animationDuration - (SystemClock.uptimeMillis() - animationStart), 0L );