Android 12 启动画面 -SplashScreen
![](https://static001.geekbang.org/infoq/39/39a6814c54c873dd48a5b286d1326c28.gif)
?? 启动画面的工作原理
============
当用户启动应用而应用的进程未在运行(冷启动)或 Activity 尚未创建(温启动)时,会发生以下事件。(在热启动期间从不显示启动画面。)
系统使用主题以及您已定义的任何动画显示启动画面。
当应用准备就绪时,会关闭启动画面并显示应用。
关于应用启动模式可以参考:Android 性能优化之启动优化。
?? 动画的元素和机制
===========
动画的元素由 Android 清单中的 XML 资源文件定义。每个元素都有浅色模式和深色模式版本。
它们由窗口背景、动画形式的应用图标和图标背景组成:
![](https://static001.geekbang.org/infoq/5c/5cf51dbb7666f187fa14d976b2fdc85f.png)
关于这些元素,请注意以下几点:
应用图标 (1) 应该是矢量可绘制对象,它可以是静态或动画形式。不超过 1000 毫秒。默认情况下,使用启动图标。
图标背景 (2) 是可选的,在图标与窗口背景之间需要更高的对比度时很有用。如果您使用一个自适应图标,当该图标与窗口背景之间的对比度足够高时,就会显示其背景。
与自适应图标一样,前景的三分之一被屏蔽(3)。
窗口背景 (4) 由不透明的单色组成。如果窗口背景已设置且为纯色,则未设置相应的属性时默认使用该背景。
启动画面动画机制由进入动画和退出动画组成。
进入动画由系统视图到启动画面组成。这由系统控制且不可自定义。
退出动画由隐藏启动画面的动画运行组成,可以对其自定义。如果你要对其进行自定义,你将可以访问 SplashScreenView 及其图标,并且可以在它们之上运行任何动画(需要设置转换、不透明度和颜色)。在这种情况下,当动画完成时,需要手动移除启动画面。
?? 自定义应用中的启动画面
==============
?? 设置主题属性以更改其外观
?? 设置启动画面背景颜色
设置了淡紫色的背景图。
效果图:
![](https://static001.geekbang.org/infoq/af/af6c9cd87e2c0e22998913f6a0f4e7b7.gif)
代码如下:
<style?name="Theme.SccMall.SplashScreen">
<item?name="windowActionBar">false</item>
<item?name="windowNoT
itle">true</item>
<item?name="android:windowSplashScreenBackground">@color/splash_screen_background</item>
</style>
AndroidManifest.xml 设置主题:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.SccMall.SplashScreen">
</application>
?? 设置中间显示的图标
中心图标大图,内容需要保留 2/3 的内边距,否则图标会被裁剪掉。
设置透明的静态图标
图标如下:
![](https://static001.geekbang.org/infoq/00/0008d5be29a8798eba2f5fe49b4354ff.png)
效果图:
![](https://static001.geekbang.org/infoq/e1/e1f527421c18dc4c9ec561bfece46e97.gif)
代码如下:
<style?name="Theme.SccMall.SplashScreen">
...
<item?name="android:windowSplashScreenAnimatedIcon">drawable/iv_splash_animation1</item>
</style>
设置透明的动态图标
效果图:
![](https://static001.geekbang.org/infoq/05/059536e817f884d3cf2e2ec51518dbf0.gif)
代码如下:
<style?name="Theme.SccMall.SplashScreen">
...
<item?name="android:windowSplashScreenAnimatedIcon">@drawable/splash_animate_icon</item>
<item?name="android:windowSplashScreenAnimationDuration">1000</item>
</style>
splash_animate_icon.xml
<?xml?version="1.0"?encoding="utf-8"?>
<animation-list?xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item?android:drawable="@drawable/iv_1"?android:duration="250"/>
<item?android:drawable="@drawable/iv_2"?android:duration="250"/>
<item?android:drawable="@drawable/iv_3"?android:duration="250"/>
<item?android:drawable="@drawable/iv_4"?android:duration="250"/>
</animation-list>
?? 设置图标的背景颜色
设置了紫色的图标背景颜色。如果图标背景不透明,会被遮挡效果无法看出。
效果图:
![](https://static001.geekbang.org/infoq/7c/7c65e3e54b23421ce51ba1d9e04bd813.png)
代码如下:
<style?name="Theme.SccMall.SplashScreen">
...
<item?name="android:windowSplashScreenIconBackgroundColor">@color/splash_screen_icon_background</item>
</style>
?? 画面底部的图片(尺寸比例需要为 2.5:1,谷歌不推荐用)
效果图:
![](https://static001.geekbang.org/infoq/02/02a4b866f89f5a940c29b6b7c95e5890.png)
这里使用的尺寸是 500:200。
代码如下:
<style?name="Theme.SccMall.SplashScreen">
...
<item?name="android:windowSplashScreenBrandingImage">@mipmap/iv_splash_screen_brandingimage</item>
</style>
?? 最终效果
![](https://static001.geekbang.org/infoq/0f/0f742062c6a339bc1cfc3fb827613756.png)
效果:
![](https://static001.geekbang.org/infoq/90/905903ac8abeaa5cd18ff5b8608eec62.gif)
代码如下:
<style?name="Theme.SccMall.SplashScreen">
<item?name="windowActionBar">false</item>
<item?name="windowNoTitle">true</item>
<item?name="android:windowSplashScreenBackground">@color/splash_screen_background</item>
<item?name="android:windowSplashScreenAnimatedIcon">@drawable/iv_splash_animation1</item>
<item?name="android:windowSplashScreenIconBackgroundColor">@color/splash_screen_icon_background</item>
<item?name="android:windowSplashScreenBrandingImage">@mipmap/iv_splash_screen_brandingimage</item>
<item?name="android:windowSplashScreenAnimationDuration">1000</item>
</style>
?? 让其在屏幕上显示更长时间
启动画面最长 1000 毫秒。如果你的广告页需要更多时间来加载数据,谷歌也提供了让其显示更长时间的方法,咱们一起来试试。
效果:
![](https://static001.geekbang.org/infoq/e6/e6d1c0746c5324d29859225026c3bb89.gif)
跟上面对比明显发现时间延长了不少。
代码如下:
public?class?AdvertiseActivity?extends?AppCompatActivity?{
@Override
protected?void?onCreate(Bundle?savedInstanceState)?{
super.onCreate(savedInstanceState);
...
//延长启动画面显示时间
extendDisplayTime();
}
//延长启动画面显示时间
private?void?extendDisplayTime()?{
MyViewModel?myViewModel?=?new?MyViewModel(getApplication());
//?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()?{
//?检查初始数据是否准备好。
if?(myViewModel.isReady())?{
//?取消挂起,内容准备好了。
content.getViewTreeObserver().removeOnPreDrawListener(this);
return?true;
}?else?{
//?挂起,内容未准备好。
return?false;
}
//如果仅 return?false,则会产生一个永久显示 SplashScreen 的效果。
}
});
}
public?class?MyViewModel?extends?AndroidViewModel?{
public?MyViewModel(Application?application)?{
super(application);
}
private?long?startUptimeMillis?=?SystemClock.uptimeMillis();
public?boolean?isReady(){
return?SystemClock.uptimeMillis()-startUptimeMillis>3000;
}
}
}
?? 自定义用于关闭启动画面的动画
效果:
![](https://static001.geekbang.org/infoq/85/85bb83b63029197d1c41c2db3711eea0.gif)
代码如下:
public?class?AdvertiseActivity?extends?AppCompatActivity?{
ActivityAdvertiseBinding?binding;
@Override
protected?void?onCreate(Bundle?savedInstanceState)?{
super.onCreate(savedInstanceState);
...
//关闭动画
spplashScreenCloseAnimation();
}
private?void?spplashScreenCloseAnimation(){
//添加一个回调,当启动画面为应用内容设置动画时调用。
getSplashScreen().setOnExitAnimationListener(splashScreenView?->?{
final?ObjectAnimator?slideUp?=?ObjectAnimator.ofFloat(
splashScreenView,
View.TRANSLATION_Y,
0f,
-splashScreenView.getHeight()
);
slideUp.setInterpolator(new?AnticipateInterpolator());
slideUp.setDuration(2000);
//?在自定义动画结束时调用 splashScreenView.remove();
slideUp.addListener(new?AnimatorListenerAdapter()?{
@Override
public?void?onAnimationEnd(Animator?animation)?{
//移除启动画面
splashScreenView.remove();
}
});
//?启动动画
slideUp.start();
});
}
}
?? 低版本适配
========
效果(Android 5.1)
![](https://static001.geekbang.org/infoq/ab/ab8e9ad4d3f08a726c216cdca0769162.gif)
因为 SplashScreen 是在 Android 12 中才新增加的功能。所以在 Android 5.1 上面没有效果。如果调用 getSplashScreen()等 Android 12 的新方法等会直接崩溃。
其实,有很多的 App 之前,就已经自己实现了 SplashScreen 功能。
那么自己实现的 SplashScreen 和官方提供的 SplashScreen 如何兼容? Android 12 是强制使用,如果不设置就使用默认应用图标。所以,如果你的代码中还保留着过去自己实现的那一套 SplashScreen,在 Android 12 中就会出现双重 SplashScreen 的现象。
因此这里可能就需要根据版本来做特殊处理了,要不就跟上面 Demo 一样,先显示一个 SplashScreenView,后面跟个 AdvertiseActivity(广告界面?)。
那如果去除自己的 AdvertiseActivity 低版本的要怎么办?我们能想到的,谷歌大佬也能想到。
Google 在 AndroidX 中提供了一个向下兼容的 SplashScreen 库。根据官方的说法,我们只要使用这个库就可以轻松解决旧版 SplashScreen 的适配问题。
?? SplashScreen 库
API 31 中引入的 SplashScreen API 的兼容类。
在 API 31+ (Android 12+) 上,此类调用平台方法。
在 API 31 之前,平台行为被复制,但启动屏幕上的动画矢量可绘制支持除外。
要使用该类,需要将启动 Activity 的主题设置为 R.style.Theme_SplashScreen 为其父级,并且需要设置 R.attr.windowSplashScreenAnimatedIcon 和 R.attr.postSplashScreenTheme 属性。
也就是说这个库是用来向下兼容,需要注意一下内容:
启动画面的中心图标动画(失效)
Activity 的主题必须以 R.style.Theme_SplashScreen 为父级
从 API 23 开始兼容所有新 Splash Screen API,图标背景除外。
?? 使用 SplashScreen 库
?? 导入库
android?{
评论