从 Android 12 开始,在所有应用的冷启动和温启动期间,系统一律会应用 Android 系统的默认启动画面。默认情况下,此系统默认启动画面由应用的启动器图标元素和主题的 windowBackground(如果是单色)构成。
如果您不迁移自己的应用,那么该应用在 Android 12 及更高版本上的启动体验将会降级,也可能会产生意外结果:
如果您的现有启动画面是使用替换 android:windowBackground 的自定义主题实现的,那么在 Android 12 及更高版本上,系统会将自定义启动画面替换为默认的 Android 系统启动画面(这可能不是应用的预期体验)。
如果您的现有启动画面是使用专用 Activity 实现的,那么在搭载 Android 12 或更高版本的设备上启动您的应用会导致系统重复显示启动画面,也就是先显示新的系统启动画面,接着显示您现有的启动画面 activity。
在Android 12上已经默认使用了SplashScreen,如果没有任何配置,会自动使用App图标。
当然也允许自定义启动画面,在value-v31中的style.xml中,可以在App的主Theme中通过如下属性来进行配置:
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<item name="android:windowSplashScreenBackground">@android:color/white</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/anim_ai_loading</item>
<item name="android:windowSplashScreenAnimationDuration">1000</item>
<item name="android:windowSplashScreenBrandingImage">@mipmap/brand</item>
</style>
windowSplashScreenBackground设置启动画面的背景色
windowSplashScreenAnimatedIcon启动图标。就是显示在启动界面中间的图片,也可以是动画
windowSplashScreenAnimationDuration设置动画的长度。注意这里最大只能1000ms,如果需要动画时间更长,则需要通过代码的手段让启动画面在屏幕上显示更长时间(下面会讲到)
windowSplashScreenIconBackground设置启动图标的背景色
windowSplashScreenBrandingImage设置要显示在启动画面底部的图片。官方设计准则建议不要使用品牌图片。
启动时长
默认当应用绘制第一帧后,启动画面会立即关闭。但是在我们实际使用中,一般在启动时进行一些初始化操作,另外大部分应用会请求启动广告,这样其实需要一些耗时的。通常情况下,这些耗时操作我们会进行异步处理,那么是否可以让启动画面等待这些初始化完成后才关闭?
我们可以使用 ViewTreeObserver.OnPreDrawListener让应用暂停绘制第一帧,直到一切准备就绪才开始,这样就会让启动画面停留更长的时间,如下:
...
var isReady = false
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
...
val content: View = findViewById(android.R.id.content)
content.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
return if (isReady) {
content.viewTreeObserver.removeOnPreDrawListener(this)
true
} else {
false
}
}
}
)
}
这样当初始化等耗时操作完成后,将isReady置为true即可关闭启动画面进入应用。
上面我们提到配置启动动画的时长最多只能是1000ms,但是通过上面的代码可以让启动画面停留更长时间,所以动画的展示时间也就更长了。
关闭动画
启动画面关闭时默认直接消失,当然我们也可以对其进行自定义。
在Activity中可以通过getSplashScreen来获取(注意判断版本,低版本中没有这个函数,会crash),然后通过它的setOnExitAnimationListener来定义关闭动画,如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
splashScreen.setOnExitAnimationListener { splashScreenView ->
val slideUp = ObjectAnimator.ofFloat(
splashScreenView,
View.TRANSLATION_Y,
0f,
-splashScreenView.height.toFloat()
)
slideUp.interpolator = AnticipateInterpolator()
slideUp.duration = 200L
//这里doOnEnd需要Android KTX库,即androidx.core:core-ktx:1.7.0
slideUp.doOnEnd { splashScreenView.remove() }
slideUp.start()
}
}
加上如上代码后,本来直接消失的启动画面就变成了向上退出了。
这里可以通过splashScreenView可以获取到启动动画的时长和开始时间,如下:
val animationDuration = splashScreenView.iconAnimationDurationMillis
val animationStart = splashScreenView.getIconAnimationStartMillis
这样就可以计算出启动动画的剩余时长。
低版本使用SplashScreen
只能在Android 12上体验官方的启动动画,显然不能够啊!官方提供了Androidx SplashScreen compat库,能够向后兼容,并可在所有 Android 版本上显示外观和风格一致的启动画面(这点我保留意见)。
首先要升级compileSdkVersion,并依赖SplashScreen库,如下:
android {
compileSdkVersion 31
...
}
dependencies {
...
implementation 'androidx.core:core-splashscreen:1.0.0-alpha01'
}
然后在style.xml添加代码如下:
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
// Set the splash screen background, animated icon, and animation duration.
<item name="windowSplashScreenBackground">@android:color/white</item>
// Use windowSplashScreenAnimatedIcon to add either a drawable or an
// animated drawable. One of these is required.
<item name="windowSplashScreenAnimatedIcon">@drawable/anim_ai_loading</item>
<item name="windowSplashScreenAnimationDuration">1000</item> # Required for
# animated icons
// Set the theme of the Activity that directly follows your splash screen.
<item name="postSplashScreenTheme">@style/AppTheme</item> # Required.
</style>
前三个我们上面都介绍过了,这里新增了一个postSplashScreenTheme,它应该设置为应用的原主题,这样会将这个主题设置给启动画面之后的Activity,这样就可以保持样式的不变。
注意上面提到的windowSplashScreenIconBackground和windowSplashScreenBrandingImage没有,这是与Android12的不同之一。
然后我们将这个style设置给Application或Activity即可:
<manifest>
<application android:theme="@style/Theme.App.Starting">
<!-- or -->
<activity android:theme="@style/Theme.App.Starting">
...
最后需要在启动activity中,先调用installSplashScreen,然后才能调用setContentView,如下
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val splashScreen = installSplashScreen()
setContentView(R.layout.activity_main)
...
然后在低版本系统上启动应用就可以看到启动画面了。
installSplashScreen这一步很重要,如果没有这一行代码,postSplashScreenTheme就无法生效,这样启动画面后Activity就无法使用之前的样式,严重的会造成崩溃。比如在Activity中存在AppCompat组件,这就需要使用AppCompat样式,否则就会Crash。
现有启动画面迁移
目前市场上的App基本都自己实现了启动页面,如果直接添加SplashScreen,就会造成重复,所以我们需要对原有启动页面进行处理。具体处理还要根据每个App自己的启动页面的实现逻辑来定
不能通过Android 12设备提供的API直接禁用默认启动屏幕。
如果您的应用程序有一个自定义启动屏幕&您不想迁移到这种新的方法,您可以尝试下面的黑客。
基本上,您要做的是覆盖res\values-v31\themes.xml中的启动屏幕主题&设置一个透明的图标。
<style name="Theme.Splash" parent="Theme.Main">
<item name="android:windowBackground">@color/background</item>
<!-- 设置一个透明的图片icon -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/transparent_image</item>
</style>
这将使你摆脱默认的应用程序图标,在应用程序启动时会出现。