Android12 Jetpack SplashScreen API总结
Posted hao_qi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android12 Jetpack SplashScreen API总结相关的知识,希望对你有一定的参考价值。
官方Android 12的Splash Screen文档地址
官方Splash Screen兼容库,支持所有版本系统
本篇文章主要围绕下面三个问题来介绍:
- 我们能从android 12 SplashScreen API里面学到什么?
- 新出的SplashScreen兼容库又是什么?能做成什么样子?
- 小甲同学:我想看Android12 SplashScreen源码,可以吗?
SplashScreen使用
首先我们需要把 compileSdk 和 targetSdk(可选) 升级到31 。
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)
true
} else {
// 挂起,内容还没有准备好
false
}
}
}
)
(C ).关闭启画面的动画
// 自己定制关闭的动画
splashScreen.setOnExitAnimationListener { splashScreenView ->
val slideUp = ObjectAnimator.ofFloat(
// 你们自己控制,自己随便写什么动画,这里我们测试让图标移动
splashScreenView.iconView,
View.TRANSLATION_Y,
0f,
-splashScreenView.height.toFloat()
)
slideUp.interpolator = AnticipateInterpolator()
slideUp.duration = 200L
slideUp.doOnEnd { splashScreenView.remove() }
slideUp.start()
}
(D).遇到的问题
- android:windowSplashScreenBrandingImage 定义的图片尺寸要求是多少?总觉得有点拉伸;
- 使用AnimationDrawable 或者 AnimatedVectorDrawable,来设置中心图标,会出现“中心图标”消失的情况,静态图标不会有这种问题出现;
- 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>
动态中心图标,不正常
我们再来看一下官方文档中的顺滑效果
下面我们使用AnimatedVectorDrawable来制作动态图标,
为了观察出效果:我们打开模拟器的开发者选项,找到Animator时长缩放设置为:动画时长x10,自己看效果,此处略……
笑脸眼睛动画的矢量图文件 👇👇,点击查看在线制作矢量图动画
<!--仅测试玩耍,感兴趣的可以自己去制作一个-->
<?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">
<path
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">
<path
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"/>
<path
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">
<path
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"/>
<path
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
SplashScreen兼容库
(A).依赖库
点击查看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).遇到的问题
兼容库目前存在的问题
- 没有android:windowSplashScreenBrandingImage这个属性
- 配置了中心图标,会裁剪成圆形
- 低版本系统不配置windowSplashScreenAnimatedIcon会出现默认的Icon
- Android12父主题设置android:windowBackground被覆盖,看不到效果
问题1: 是因为兼容库的layout文件目录下面的splash_screen_view.xml没有“品牌名的视图”,大家点击查看一下,两个布局的xml内容就知道了:
frameworks下面的Android12的splash_screen_view.xml
core-splashscreen下面的兼容库的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).制作一个启动页
- 模仿快手App的启动页
只需要配置父主题的android:windowBackground
Android5.0 ~ Android11 效果
由于我们在文章上面介绍到Android12上,无法为SplashScreen设置父主题的android:windowBackground,但我们依然可以通过配置静态中心图标来做到一样的效果的,请看下面的效果:
Android12 效果
如果你的UI设计师,给你矢量图,那么你就可以让中心图标在Android12系统上动起来了😆另外,可以建议UI设计师:统一所有系统上,启动页“中心”图标,居中展示,不然会有点怪
- 动态图标启动页
如果设计成动态启动图标,这个需要考虑2个因素:
一、 低版本系统表现效果不一致,有些系统里面,动态图标只在启动页关闭的时候才显示(亲测Android平板5.1.1系统就是这样的);
二、如何兼容低版本系统,是先展示底部品牌名,最后只能等动态图标显示咯?
如果喜欢折腾的同学,可以多测试测试,我在使用Android10.0测试动态图标的时候,效果看着还可以,具体系统下限在哪?
这个看你们产品设计定位了,还有测试妹妹是否同意你们用,效果是否符合你们的产品;
建议的做法是:
- Android 5.0 ~ Android 11.0系统,都统一使用android:windowBackground配置启动页背景
- 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 Jetpack 基准测试库测量结果?
Android Jetpack Paging 3:带 Room 的 PagingSource
Jetpack新成员SplashScreen:为全新的应用启动效果赋能!