前言
在移动应用开发中,启动画面是用户第一次接触到应用的重要组成部分,也是第一印象。然而,许多 Android 应用在启动阶段都面临着一个普遍的问题:白屏。为了改善这一问题,我们通常的做法都是自定义一个单独的启动页面,但这并没有解决根本问题。
于是谷歌在 Android 12 系统上引入 SplashScreen API,它可以让开发者轻松的添加应用画面,开发者只需要添加我们的应用图标以及定义动画的进出场,便可解决白屏问题。
SplashScreen
虽然 SplashScreen 的登场解决了白屏问题,但是它仅支持 Android 12及以上版本,但是在 12 以下的手机系统还占有着很大一部分,于是乎 Core SplashScreen 出现了,它属于 Jetpack 组件中的一员,用于适配低版本的手机系统。
工作原理
首先应用的启动情况分为三种情况:冷启动、温启动、热启动。
- 冷启动:当首次启动或者应用被系统杀死之后应用就属于冷启动,这时会显示我们定义的启动画面。
- 温启动:应用已经创建,但是 activity 已经被销毁了,这是启动应用它会重新创建 activity 这属于温启动,由于 activity 销毁重建所以它也是会显示启动画面。
- 热启动:当应用已经创建并且用户点击 Home 键后应用会进入系统后台这时再次启动就属于热启动,此时不会再显示启动画面。
示例
1.添加依赖
首先在模块级别的 build.gradle 文件中引入依赖。
implementation "androidx.core:core-splashscreen:1.0.1"
2.添加及配置元素
为了给图标添加动画效果,官方提供了两种选项,这里使用第二种 AnimatedVectorDrawable,该选项可以为尺量对象的属性添加动画效果。
官方文档
您通常在三个 XML 文件中定义添加动画效果之后的矢量可绘制对象:
- 一个矢量可绘制对象, 元素位于 res/drawable 目录下。
- 一个添加动画的矢量可绘制对象, 位于 res/drawable 目录下。
- 一个或多个对象 Animator, 位于 res/animator 目录下。
添加我们的应用图标,这里我直接从官方资源 Vector Asset 中随便引入一个,重命名替换为 logo ,尺寸方面我选择的是 50x50。
启动页面的图标可以是静态或者动态,这里以简单的旋转缩放动画为例,当应用启动时,图标从中心点开始旋转 360° 并且从 0.0 缩放到 0.4。
使用 定义一组路径,注意有些图标的绘制是不止一条
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="50dp"
android:height="50dp"
android:tint="#08FF04"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- 整个logo设置为组 -->
<group
android:name="animationGroup"
android:pivotX="12"
android:pivotY="12">
<!-- 如果有多个Path,请全部包裹 -->
<path
android:fillColor="@android:color/white"
android:pathData="M17.6,9.48l1.84,-3.18c0.16,-0.31 0.04,-0.69 -0.26,-0.85c-0.29,-0.15 -0.65,-0.06 -0.83,0.22l-1.88,3.24c-2.86,-1.21 -6.08,-1.21 -8.94,0L5.65,5.67c-0.19,-0.29 -0.58,-0.38 -0.87,-0.2C4.5,5.65 4.41,6.01 4.56,6.3L6.4,9.48C3.3,11.25 1.28,14.44 1,18h22C22.72,14.44 20.7,11.25 17.6,9.48zM7,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25S8.25,13.31 8.25,14C8.25,14.69 7.69,15.25 7,15.25zM17,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25s1.25,0.56 1.25,1.25C18.25,14.69 17.69,15.25 17,15.25z" />
</group>
</vector>
创建 Animator 动画对象,通过属性给图标配置动画。
官方建议动画时长不要超过1000毫秒(1秒钟),代码中我设置了运行时长为1秒以及旋转和缩放动画,启动时图标从中心点 0° 旋转 360°,并且从 XY 轴从 0 倍缩放到 0.4 倍。
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000">
<!-- 旋转动画 -->
<propertyValuesHolder
android:propertyName="rotation"
android:valueFrom="0.0"
android:valueTo="360.0"
android:valueType="floatType" />
<!-- X轴缩放动画 -->
<propertyValuesHolder
android:propertyName="scaleX"
android:valueFrom="0.0"
android:valueTo="0.4"
android:valueType="floatType" />
<!-- Y轴旋转动画 -->
<propertyValuesHolder
android:propertyName="scaleY"
android:valueFrom="0.0"
android:valueTo="0.4"
android:valueType="floatType" />
</objectAnimator>
接下来图标和动画属性两个文件都有了,使用动画矢量把他们组合到一起即可,编译器右侧可以播放动画来预览我们设置的效果。
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/logo"><!-- 引用图标 -->
<!-- 引用动画属性和设定的组 -->
<target
android:animation="@animator/logo_animator"
android:name="animationGroup"/>
</animated-vector>
3.创建启动主题
在 values/theme.xml 文件中自定义启动主题,它用于替换掉 MainActivity 原有的默认主题并且在启动完成之后自动切换回我们默认的主题。注意:还需要创建一个 SDK-31的 theme.xml ,用于高版本。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 默认主题 -->
<style name="Theme.AnimationsSplashScreenApi" parent="android:Theme.Material.Light.NoActionBar" />
<!-- 启动主题(低版本使用),高版本用的是 -v31 -->
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
<item name="windowSplashScreenAnimatedIcon">@drawable/animated_logo</item>
<item name="postSplashScreenTheme">@style/Theme.AnimationsSplashScreenApi</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
<item name="android:windowSplashScreenAnimatedIcon">@drawable/animated_logo</item>
<item name="postSplashScreenTheme">@style/Theme.AnimationsSplashScreenApi</item>
</style>
</resources>
两者唯一的区别在于高版本使用的是带有 android: 的属性,低版本不具备动画效果,但是会显示固定的图标。
4.应用主题
在清单文件中给替换掉默认的应用程序主题和 MainActivity 主题。
<application
...
android:theme="@style/Theme.App.Starting"
tools:targetApi="31">
<activity
...
android:theme="@style/Theme.App.Starting">
...
</activity>
</application>
5.installSplashScreen
在启动Activity时先调用installSplashScreen(),然后再调用 super.onCreate()。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//安装启动页
installSplashScreen()
setContent {
...
}
}
}
到这里一个带有动画的启动页就弄好了,但是还不够结合实际情况,应用在启动时都会去网络或者本地加载数据,这期间用户进入到了首页而没有数据展示,就处于非常尴尬的局面,所以需要优化一下。
setKeepOnScreenCondition() 函数用于配置启动页停留的条件,比如当数据加载完成之后才进入主界面。
//创建ViewModel,模拟网络加载数据
class MainViewModel : ViewModel() {
private val _isReady = mutableStateOf(false)
val isReady: State<Boolean> = _isReady
init {
viewModelScope.launch {
delay(3000L)
_isReady.value = true
}
}
}
// MainActivity
class MainActivity : ComponentActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen().apply {
//设置停留在闪屏页上的条件
//当条件为false的时候会一直停留反之进入主界面
setKeepOnScreenCondition {
!viewModel.isReady.value
}
}
setContent {
...
}
}
}
6.运行效果
关注我,与你分享更多技术文章。麻烦右下角区域点个【赞】支持一下吧!更多内容请关注下方微信公众号。