官方Android 12的 Splash Screen文档地址

https://developer.android.com/guide/topics/ui/splash-screen

官方Splash Screen兼容库 ,支持所有版本系统。

https://developer.android.com/guide/topics/ui/splash-screen/migrate

本篇文章主要围绕下面三个问题来介绍:

1、我们能从Android 12 SplashScreen API里面学到什么?

2、新出的SplashScreen兼容库又是什么?能做成什么样子?

3、小甲同学:我想看Android12 SplashScreen源码,可以吗?

进入正题,我们先介绍:SplashScreen如何使用,以及目前会遇到的问题,如何无缝过渡? 会出现什么问题,怎么解决?

SplashScreen使用

首先我们需要把compileSdk和targetSdk(可选)升级到31。

2.1.Android12版本

(A).主题和外观配置

<!--文章末尾我们会把包含所有示例的链接地址提供出来,如有需要:请翻到文章末尾-->

<!-- values-v31/themes.xml -->

<!--单一颜色填充「启动画面」窗口背景-->

< item name = "android:windowSplashScreenBackground" > @color/... </ item >

<!--「启动画面」中心的图标,

可以配置AnimationDrawable 和 AnimatedVectorDrawable类型的drawable-->

< item name = "android:windowSplashScreenAnimatedIcon" > @drawable/... </ item >

<!--「启动画面」中心图标动画的持续时间,这个属性不会对屏幕显示的实际时间产生任何影响-->

< item name = "android:windowSplashScreenAnimationDuration" > 1000 </ item >

<!--「启动画面」中心图标后面设置背景-->

< item name = "android:windowSplashScreenIconBackgroundColor" > @color/... </ item >

<!--「启动画面」底部显示的品牌图标-->

< item name = "android:windowSplashScreenBrandingImage" > @drawable/... </ item >

(B).延长启动画面

val content: View = findViewById(android.R.id.content)

content.viewTreeObserver.addOnPreDrawListener(

object : ViewTreeObserver.OnPreDrawListener {

override fun onPreDraw : Boolean {

// 模拟一些数据的初始化,再取消挂起

return if (viewModel.isReady) {

// 取消挂起,恢复页面内容绘制

content.viewTreeObserver.removeOnPreDrawListener( this )

} else {

// 挂起,内容还没有准备好

false

(C).关闭启画面的动画

// 自己定制关闭的动画

splashScreen.setOnExitAnimationListener { splashScreenView ->

val slideUp = ObjectAnimator.ofFloat(

// 你们自己控制,自己随便写什么动画,这里我们测试让图标移动

splashScreenView.iconView,

View.TRANSLATION_Y,

-splashScreenView.height.toFloat

slideUp.interpolator = AnticipateInterpolator

slideUp.duration = 200 L

slideUp.doOnEnd { splashScreenView. remove }

slideUp.start

(D).遇到的问题

1、 android:windowSplashScreenBrandingImage 定义的图片尺寸要求是多少?总觉得有点拉伸;

2、使用 AnimationDrawable 或者 AnimatedVectorDrawable ,来设置中心图标,会出现“中心图标”消失的情况,静态图标不会有这种问题出现;

3、Android12父主题设置 android:windowBackground 被覆盖,看不到效果。

问题1 : 在源码里面也没有看到具体的值或者比例大小,怎么办呢?

小技巧: 使用一个超大的正方形的图标设置进去测试了一下,拉伸不要紧,我们要的是比例, 然后测量了一下比例为:2.5 : 1,所以设计品牌名图标的时候,可以设置为400 * 160这样的比例为2.5:1的图标。

问题2 : 针对中心图标会闪现消失的问题做测试,

测试一:静态Icon、测试二:动态Icon

静态中心图标 - 正常

下面我们来测试动态中心图标,为了方便测试出效果,我们覆盖住图标后面的背景色,方便对比,最后发现:测试结果不太理想,效果不行。

<!--AnimationDrawable写法-->

<!--没有真机测试,这个写法,效果看起来也挺奇怪的,可能是模拟器且是预览版的问题吧-->

< animation-list xmlns:android = "http://schemas.android.com/apk/res/android"

android:oneshot = "true" >

< item android:drawable = "@mipmap/ic_launcher" android:duration = "600" />

< item android:drawable = "@drawable/api12_logo" android:duration = "200" />

< item android:drawable = "@mipmap/ic_launcher" android:duration = "200" />

</ animation-list >

动态中心图标,不正常

仔细看图标后面的「蓝色背景」

我们再来看一下官方文档中的顺滑效果。

官方效果顺滑

对比官方的效果,猜测可能是模拟器和预览版Android12的问题,主要是没有真机来测试Android12这个效果,不过这难不到我们,如果你的模拟器也有同样问题,请跟着我们做如下操作:

下面我们使用 AnimatedVectorDrawable 来制作动态图标,为了观察出效果:我们打开模拟器的开发者选项,找到Animator时长缩放设置为:动画时长x10,来往下看效果:

10倍慢放 - 看着才正常

笑脸眼睛动画的矢量图文件 ,点击查看 在线制作矢量图动画

https://shapeshifter.design/

<!--仅测试玩耍,感兴趣的可以自己去制作一个-->

<?xml version="1.0" encoding="utf-8"?>

< animated-vector

xmlns:android = "http://schemas.android.com/apk/res/android"

xmlns:aapt = "http://schemas.android.com/aapt" >

< aapt:attr name = "android:drawable" >

< vector

android:name = "vector"

android:width = "24dp"

android:height = "24dp"

android:viewportWidth = "24"

android:viewportHeight = "24" >

< group android:name = "group" >

android:name = "path_4"

android:pathData = "M 11.99 2 C 6.47 2 2 6.48 2 12 C 2 17.52 6.47 22 11.99 22 C 17.52 22 22 17.52 22 12 C 22 6.48 17.52 2 11.99 2 Z M 12 20 C 7.58 20 4 16.42 4 12 C 4 7.58 7.58 4 12 4 C 16.42 4 20 7.58 20 12 C 20 16.42 16.42 20 12 20 Z M 12 17.5 C 14.33 17.5 16.32 16.05 17.12 14 L 15.45 14 C 14.76 15.19 13.48 16 12 16 C 10.52 16 9.25 15.19 8.55 14 L 6.88 14 C 7.68 16.05 9.67 17.5 12 17.5 Z"

android:fillColor = "#FFFFFF" />

</ group >

< group android:name = "group_1" >

android:name = "path_1"

android:pathData = "M 8.5 9.5 M 7 9.5 C 7 9.102 7.158 8.721 7.439 8.439 C 7.721 8.158 8.102 8 8.5 8 C 8.898 8 9.279 8.158 9.561 8.439 C 9.842 8.721 10 9.102 10 9.5 C 10 9.898 9.842 10.279 9.561 10.561 C 9.279 10.842 8.898 11 8.5 11 C 8.102 11 7.721 10.842 7.439 10.561 C 7.158 10.279 7 9.898 7 9.5"

android:fillColor = "#FFFFFF" />

android:name = "path_3"

android:pathData = "M 8.5 9.5 M 7 9.5 C 7 9.102 7.158 8.721 7.439 8.439 C 7.721 8.158 8.102 8 8.5 8 C 8.898 8 9.279 8.158 9.561 8.439 C 9.842 8.721 10 9.102 10 9.5 C 10 9.898 9.842 10.279 9.561 10.561 C 9.279 10.842 8.898 11 8.5 11 C 8.102 11 7.721 10.842 7.439 10.561 C 7.158 10.279 7 9.898 7 9.5"

android:fillColor = "#FFFFFF" />

</ group >

< group android:name = "group_2" >

android:name = "path"

android:pathData = "M 15.5 9.5 M 14 9.5 C 14 9.102 14.158 8.721 14.439 8.439 C 14.721 8.158 15.102 8 15.5 8 C 15.898 8 16.279 8.158 16.561 8.439 C 16.842 8.721 17 9.102 17 9.5 C 17 9.898 16.842 10.279 16.561 10.561 C 16.279 10.842 15.898 11 15.5 11 C 15.102 11 14.721 10.842 14.439 10.561 C 14.158 10.279 14 9.898 14 9.5"

android:fillColor = "#FFFFFF" />

android:name = "path_2"

android:pathData = "M 15.5 9.5 M 14 9.5 C 14 9.102 14.158 8.721 14.439 8.439 C 14.721 8.158 15.102 8 15.5 8 C 15.898 8 16.279 8.158 16.561 8.439 C 16.842 8.721 17 9.102 17 9.5 C 17 9.898 16.842 10.279 16.561 10.561 C 16.279 10.842 15.898 11 15.5 11 C 15.102 11 14.721 10.842 14.439 10.561 C 14.158 10.279 14 9.898 14 9.5"

android:fillColor = "#FFFFFF" />

</ group >

</ vector >

</ aapt:attr >

< target android:name = "group_1" >

< aapt:attr name = "android:animation" >

< objectAnimator

android:propertyName = "translateX"

android:duration = "1000"

android:valueFrom = "0"

android:valueTo = "7"

android:valueType = "floatType"

android:interpolator = "@android:interpolator/fast_out_slow_in" />

</ aapt:attr >

</ target >

< target android:name = "group_2" >

< aapt:attr name = "android:animation" >

< objectAnimator

android:propertyName = "translateX"

android:duration = "1000"

android:valueFrom = "0"

android:valueTo = "-7"

android:valueType = "floatType"

android:interpolator = "@android:interpolator/fast_out_slow_in" />

</ aapt:attr >

</ target >

</ animated-vector >

后来我们又用了 AnimationDrawable 测试了一下慢放效果也不行,你仔细想一下:图片轮播放效果能好吗?

所以: AnimationDrawable AnimatedVectorDrawable 为矢量图添加动画效果。

问题3 :Android12父主题设置 android:windowBackground 被覆盖,看不到效果。

不要紧,只要我们的UI设计师(美工)按照如下尺寸规范来设计,使用静态中心图标,一样可以实现同样效果:

中心图标 : 图标内容区域内边距2/3,防止元素被切。

品牌名图标 : 设计的尺寸比例为:2.5:1。

2.2.SplashScreen兼容库

点击查看 官方Splash Screen兼容库文档

https://developer.android.com/guide/topics/ui/splash-screen/migrate

(A).依赖库

点击查看 Core库里面的最新版本

https://developer.android.com/jetpack/androidx/releases/core

// 可在所有Android版本上使用的兼容库

implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'

(B).主题和外观配置

定义Activity应该使用的主题

< style name = "Theme.App" parent = "Theme.MaterialComponents.xxxxx.DarkActionBar" >

< item name = "android:windowBackground" > @color/... </ item >

< item name = "android:statusBarColor" > @android:color/transparent </ item >

< item name = "android:windowLightStatusBar" tools:targetApi = "m" > ...... </ item >

< item name = "android:navigationBarColor" > @android:color/transparent </ item >

</ style >

创建父主题给启动画面使用

< style name = "Theme.App.Starting" parent = "Theme.SplashScreen.IconBackground" >

< item name = "android:windowBackground" > @drawable/... </ item >

< item name = "windowSplashScreenBackground" > @color/... </ item >

< item name = "windowSplashScreenAnimationDuration" > 200 </ item >

< item name = "postSplashScreenTheme" > @style/Theme.App </ item >

</ style >

AndroidManifest.xml配置Activity的主题

< manifest >

< application android:theme = "@style/Theme.App.Starting" >

<!-- application和activity,两个选一个配置: @style/Theme.App.Starting -->

< activity android:theme = "@style/Theme.App.Starting" >

(C).初始化SplashScreen

override fun onCreate (savedInstanceState: Bundle ?) {

super .onCreate(savedInstanceState)

val splashScreen = installSplashScreen

setContent { ...... }

splashScreen.setKeepVisibleCondition {

!mainViewModel.mockDataLoading

splashScreen.setOnExitAnimationListener( this )

(D).中心图标大小修改

< item name = "splashScreenIconSize" > @dimen/.... </ item >

(E).遇到的问题

兼容库目前存在的问题:

1、没有 android:windowSplashScreenBrandingImage 这个属性。

2、配置了中心图标,会裁剪成圆形。

3、低版本系统不配置 windowSplashScreenAnimatedIcon 会出现默认的Icon。

4、Android12父主题设置 android:windowBackground 被覆盖,看不到效果。

问题1 : 是因为兼容库的layout文件目录下面的 splash_screen_view.xml 没有“品牌名的视图”,大家点击查看一下,两个布局的xml内容就知道了:

frameworks下面的 Android12的splash_screen_view.xml

https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/layout/splash_screen_view.xml

core-splashscreen下面的 兼容库的splash_screen_view.xml

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:core/core-splashscreen/src/main/res/layout/splash_screen_view.xml

但是我们在Android12即values-v31的 themes.xml 里面依然可以配置 android:windowSplashScreenBrandingImage 这个属性,因为Android12的SplashScreen是集成在frameworks里面的。

问题2 : 是因为兼容库里面使用了 MaskedDrawable 包装了Icon,会裁剪成圆形,图标内容设计要保留2/3的内边距,否则会出现内容被裁剪掉的问题。

如何修复这个裁剪圆形问题呢?

把源码拷贝出来,总共就3个源代码文件,自己复制出来修改删除也可以的。或者,图标设计准则为:内容保留内边距为2/3,防止元素被裁剪。

问题3 : 写一个透明的 drawable.xml 然后替换就行了,类似如下方式:

<?xml version="1.0" encoding="utf-8"?>

< shape android:shape = "rectangle" xmlns:android = "http://schemas.android.com/apk/res/android" >

< solid android:color = "@android:color/transparent" />

< size android:width = "0dp" android:height = "0dp" />

</ shape >

问题4 :Android12父主题设置 android:windowBackground 被覆盖,看不到效果。

不要紧,只要我们的UI设计师(美工)按照如下尺寸规范来设计,使用静态中心图标,一样可以实现同样效果:

中心图标 : 图标内容区域内边距2/3,防止元素被切。

品牌名图标 : 设计的尺寸比例为:2.5:1。

(F).制作一个启动页

1.模仿快手App的启动页

只需要配置父主题的 android:windowBackground

Android5.0 ~ Android11 效果

由于我们在文章上面介绍到Android12上,无法为SplashScreen设置父主题的 android:windowBackground ,但我们依然可以通过配置静态中心图标来做到一样的效果的,请看下面的效果:

Android12 效果

如果你的UI设计师,给你矢量图,那么你就可以让中心图标在Android12系统上动起来了。

另外,可以建议UI设计师:统一所有系统上,启动页“中心”图标,居中展示,不然会有点怪。

2.动态图标启动页

如果设计成动态启动图标,这个需要考虑2个因素:

一、 低版本系统表现效果不一致,有些系统里面,动态图标只在启动页关闭的时候才显示(亲测Android平板5.1.1系统就是这样的)。

二、 如何兼容低版本系统,是先展示底部品牌名,最后只能等动态图标显示咯?

如果喜欢折腾的同学,可以多测试测试,我在使用Android10.0测试动态图标的时候,效果看着还可以,具体系统下限在哪?

这个看你们产品设计定位了,还有测试妹妹是否同意你们用,效果是否符合你们的产品。

建议的做法是:

1、Android 5.0 ~ Android 11.0系统,都统一使用 android:windowBackground 配置启动页背景。

2、Android12.0 如果UI设计师给你做了矢量图,你可以做动态的中心图标,不给你,使用静态图标也可以的,参考上面:模拟快手App启动页。

为了在模拟器上能正常显示出效果,我们在模拟器的开发者选项,找到Animator时长缩放设置为:动画时长x10,放慢10倍,缺真机测试。

Android12 动态启动页图标

我们这里只分析Android12 SplashScreen,兼容库没有太多内容不足500行,感兴趣的同学可以自己阅读一下。

我们在XXXActivity里面第一次用到了 splashScreen.setOnExitAnimationListener ,从这里开始往源头开始找,在下面的方法初始化了SplashScreen。

//android.app.Activity

private SplashScreen getOrCreateSplashScreen {

synchronized ( this ) {

if (mSplashScreen == null ) {

mSplashScreen = new SplashScreen.SplashScreenImpl( this );

return mSplashScreen;

我们来看 SplashScreenImpl 实现类。

//android.app.Activity

class SplashScreenImpl implements SplashScreen {

......

//把SplashScreenImpl添加到这个单例类里面

private final SplashScreenManagerGlobal mGlobal;

public SplashScreenImpl (Context context) {

mGlobal = SplashScreenManagerGlobal.getInstance;

@Override

public void setOnExitAnimationListener (@NonNull SplashScreen.OnExitAnimationListener listener) {

......

mGlobal.addImpl( this ); // 用于后面执行启动画面将退出的回调

......

public void setSplashScreenTheme (@StyleRes int themeId) {

......

try {

//设置启动画面的主题

AppGlobals.getPackageManager.setSplashScreenTheme(......);

} catch (RemoteException e) {

Log.w(TAG, "Couldn't persist the starting theme" , e);

// 启动画面管理器

class SplashScreenManagerGlobal {

......

// 管理多个闪屏实现

private final ArrayList<SplashScreenImpl> mImpls = new ArrayList<>;

private SplashScreenManagerGlobal {

// 向此进程注册启动画面管理器

ActivityThread.currentActivityThread.registerSplashScreenManager( this );

......

private static final Singleton<SplashScreenManagerGlobal> sInstance =

new Singleton<SplashScreenManagerGlobal> {

@Override

protected SplashScreenManagerGlobal create {

return new SplashScreenManagerGlobal;

private void addImpl (SplashScreenImpl impl) {

synchronized (mGlobalLock) {

mImpls.add(impl);

private void removeImpl (SplashScreenImpl impl) {

synchronized (mGlobalLock) {

mImpls.remove(impl);

......

public void handOverSplashScreenView (IBinder token,SplashScreenView splashScreenView) {

//调用的是 => splashScreenView.transferSurface;

transferSurface(splashScreenView);

//回调 => impl.mExitAnimationListener.onSplashScreenExit(view);

dispatchOnExitAnimation(token, splashScreenView);

......

我们看到初始化 SplashScreenManagerGlobal 的时候,向此进程注册启动画面管理器。

//android.app.ActivityThread

public void registerSplashScreenManager (SplashScreen.SplashScreenManagerGlobal manager) {

synchronized ( this ) {

mSplashScreenGlobal = manager;

如何把SplashScreen添加到当前的窗口的呢?

ActivityThread继承 ClientTransactionHandler ,里面有一个这样的抽象方法:

//android.app.ClientTransactionHandler

public abstract void handleAttachSplashScreenView ( @NonNull ActivityClientRecord r,

@NonNull SplashScreenViewParcelable parcelable ) ;

ActivityThread肯定会实现这个方法,那么是谁调用了它呢?由于篇幅问题,就不一行一行代码的去介绍分析了,感兴趣的同学,可以自己深入研究,我们下面贴出来调用的流程图。

谁调用了 handleAttachSplashScreenView ,它的调用链流程图如下:

好了,看完流程图,我们再看一下 ActivityThread#handleAttachSplashScreenView

//android.app.ActivityThread

@Override

public void handleAttachSplashScreenView (@NonNull ActivityClientRecord r,

@Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {

final DecorView decorView = (DecorView) r.window.peekDecorView;

if (parcelable != null && decorView != null ) {

createSplashScreen(r, decorView, parcelable);

......

private void createSplashScreen (ActivityClientRecord r, DecorView decorView,

SplashScreenView.SplashScreenViewParcelable parcelable) {

// 初始化SplashScreenView构建器

final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);

// 从parcelable中获取配置数据,并通过build初始化SplashScreenView,设置数据

final SplashScreenView view = builder.createFromParcel(parcelable).build;

// 把SplashScreenView添加到DecorView中

decorView.addView(view);

// 设置SystemUI颜色

view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);

// 刷新视图

view.requestLayout;

......

核心的部分源码已经分析差不多了,剩下的一些源码,感兴趣的同学可以自己查看阅读。

compileSdk升级到31

产品中统一使用兼容库SplashScreen

implementation 'androidx.core:core-splashscreen:最新版本'

演示示例中资源目录

drawable —— 定义低版本的drawable资源

drawable-v23 —— 定义 6.0 以上的资源

drawable-v31 —— 定义Android12及以上的资源

values —— 定义默认资源

values -night —— 定义默认深色模式资源

values -v23 —— 定义 6.0 以上系统资源

values -v31 —— 定义Android12及以上的资源

values -night-v31 —— 定义Android12及以上的深色模式资源

启动页图标设计准则

中心图标大图,内容需要保留2/3的内边距,否则图标会被裁剪掉,另外:图标尺寸大小可以修改。

底部品牌名图标:尺寸比例需要为 2.5:1。

兼容库SplashScreen低版本不支持设置底部品牌图标。

Android12需要在values-v31目录手动添加如下属性,才可以显示品牌名图标。

<!--兼容库没有这个属性,我们需要在values-v31单独配置一下-->

< item name = "android:windowSplashScreenBrandingImage" > @drawable/... </ item >

Android12以下系统可以使用 android:windowBackground 为父主题设置窗口背景,切记不要在Android12及以上系统设置父主题的窗口背景(因为没有效果)。

Android12系统以下系统,使用 android:windowBackground 的话,一定要给 windowSplashScreenAnimatedIcon 设置一个透明的drawable,否则会出现机器人图标。

windowSplashScreenBackground 这个属性的颜色一定要注意,配置有问题的话,启动页过渡到主页面的时候,会有这个颜色闪出来,建议和Activity的 android:windowBackground 配置成一样的颜色。

在启动画面上面,添加一个“广告或者推广页面”,代码和效果如下:

override fun onSplashScreenExit (splashScreenViewProvider: SplashScreenViewProvider ) {

if (splashScreenViewProvider.view is ViewGroup){

// 在这里添加一个启动页广告或者推广页面

val composeView = ComposeView( this @SplashScreenCompatActivity ).apply {

setContent {

SplashAdScreen(onCloseAd = {

splashScreenViewProvider.remove

(splashScreenViewProvider.view as ViewGroup).addView(composeView)

return

实践 - 启动页添加广告或者推广页

文章中示例的演示APK及源码地址:

静态图标启动页 动态图标启动页

(Android12系统有动画效果)

启动页加广告

下载: SplashScreen快手启动页效果的apk001

https://wws.lanzoui.com/iV0M5vexz7a

下载: SplashScreen快手启动页效果的apk002

https://wws.lanzoui.com/ijLzZvezk1g

下载: SplashScreen启动页广告apk

https://wws.lanzoui.com/igAufvftyfe

提取码:7gj2 提取码:b6ce 提取码:fnva

点击查看: SplashScreen演示示例的源码

https://github.com/TheMelody/SplashScreenExample

在线流程图制作

https://www.bullmind.com/

官方文档 Splash screens

https://developer.android.com/guide/topics/graphics/vector-drawable-resources

Google官网介绍矢量图

https://developer.android.com/guide/topics/graphics/vector-drawable-resources.html

在线svg编辑器

https://editor.method.ac/

在线制作矢量图动画

https://shapeshifter.design/

在线合并多个GIF制作

https://ezgif.com/combine

最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!

再见,内存泄漏!

新技术又又叒叒叒来了?

Android包体积常规、进阶、极致优化方案分享

点击 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~

┏(^0^)┛明天见! 返回搜狐,查看更多

责任编辑:

声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。