早期的Android上App的啓動速度常爲人詬病,現在的啓動表現已不遜iOS。Google針對系統的不斷優化絕對功不可沒,從8.0獨立出來的
SplashWindow
,到12上推出的全新SplashScreen
。java
在App的主要內容展現以前,按照需求的不一樣,或多或少會先展現這樣幾個畫面。android
畫面 | 用途 |
---|---|
Splash Screen | 展現品牌Logo或Slogan |
Advertisement Screen | 展現節日活動或平常廣告 |
Guide Screen | 演示重點功能,通常只展現一次 |
咱們經常花費精力去打造引導畫面或廣告畫面,而做爲第一印象的啓動畫面卻容易被忽視。回想下之前都是怎麼處理這個畫面的:git
通常經過設置windowSplashscreenContent
屬性來展現UI提供的啓動圖,系統將爲它建立專門的Windowgithub
假使忘記設置這個屬性的話,默認的白色背景將致使啓動過程當中會有個白畫面一閃而過markdown
要去掉這個突兀的白畫面可不能簡單地設置Background爲null,否則一閃而過的又會變成黑畫面app
最終發現windowDisablePreview
屬性能夠完全關閉這個畫面,這樣一來確實沒有任何突兀的畫面一閃而過了async
但這又會帶來啓動"變慢"的反作用,由於用來過渡的啓動畫面被關閉以後,App描畫前屏幕幾乎沒有什麼變化。即使App性能沒有劣化,但爲了留住用戶,咱們仍是得好好對待這個啓動畫面。ide
然而現有的windowSplashscreenContent
可供定製的空間着實有限。也許官方也注意到了這點,便精心設計了Splash Screen
API,並在Android 12
裏重磅推出。oop
有了這個全新特性的幫助,啓動畫面的定製將更加自由、方便。先來看下采用SplashScreen API 快速定製的啓動效果。性能
下面將逐步演示全新SplashScreen可供定製的各個方面。
採用xml便可快速定製各式進入效果。
默認狀況下啓動畫面將展現白色背景和Launcher上的Adaptive Icon
,也是不錯的,比之前的白畫面要好不少。
替換Icon爲Adaptive Icon的前景圖,背景色微調爲米黃色。
<item name="android:windowSplashScreenBackground">@color/newSplashScreenColor</item>
<item name="android:windowSplashScreenAnimatableIcon">@drawable/ic_kotlin_hero_new</item>
複製代碼
Icon色調和畫面背景色的對比不夠明顯的狀況下,能夠添加Icon背景色增強辨識度。
<item name=」android:windowSplashScreenIconBackground」>@color/newSplashIconMaskColor</item>
複製代碼
添加品牌Logo能夠展現企業形象或Slogan,使得啓動畫面更爲完整和精細。
<item name=」android:windowSplashScreenBrandingImage」>@drawable/ic_tm_brand_newer</item>
複製代碼
動畫形式的Icon能夠增添設計和創意,使得啓動流程更加流暢和有趣。
<item name="android:windowSplashScreenAnimatableIcon">@drawable/ic_kotlin_hero_new_animated_rotate</item>
<item name="android:windowSplashScreenAnimationDuration">@integer/icon_animator_duration</item>
複製代碼
好比讓機器人圖標旋轉起來。
再好比讓機器人在Kotlin上側滑。
或者讓幾何圖案拼湊出字母K以後和機器人匯合,象徵着Android
和Kotlin
的強強聯合。
注意:
1000ms
。The splash screen is dismissed as soon as your app draws its first frame. If you need to load a small amount of data such as in-app theme settings from a local disk asynchronously, you can use ViewTreeObserver.OnPreDrawListener to suspend the app to draw its first frame.
後臺數據的加載不免耗時,啓動畫面結束了主要內容仍未加載好的話,體驗不是太好。可以控制啓動畫面的持續時時長就行了。
現有的ViewTreeObserver的OnPreDrawListener
回調是能夠掛起描畫的,若是咱們在數據準備好以後再放行描畫,就能夠間接地延長啓動畫面的顯示。
好比Activity初始化2s後才放行描畫。
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
keepSplashScreenLonger()
}
private fun keepSplashScreenLonger() {
// 監聽Content View的描畫時機
val content: View = findViewById(android.R.id.content)
content.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
// 準備好了描畫放行,反之掛起
return if (viewModel.isDataReady()) {
content.viewTreeObserver.removeOnPreDrawListener(this)
true
} else {
false
}
}
}
)
}
}
class MyViewModel(application: Application): AndroidViewModel(application) {
companion object {
const val WORK_DURATION = 2000L
}
private val initTime = SystemClock.uptimeMillis()
fun isDataReady() = SystemClock.uptimeMillis() - initTime > WORK_DURATION
}
複製代碼
看一下效果,發現啓動畫面的展現時間確實變長了。
當App的第一幀開始描畫,SplashScreen
將會退出展現。爲了豐富退出環節的體驗,系統也開放了相應的入口,即畫面退出的回調。在這個回調裏能夠開始退出效果的定製,包括總體的退出動畫和圖標的退出動畫。
向SplashScreen註冊OnExitAnimationListener接口便可監聽啓動畫面的退出。
override fun onCreate(savedInstanceState: Bundle?) {
...
customizeSplashScreenExit()
}
private fun customizeSplashScreenExit() {
splashScreen.setOnExitAnimationListener { splashScreenView ->
Log.d("Splash", "SplashScreen#onSplashScreenExit view:$splashScreenView")
sleep(1000)
Log.d("Splash", "SplashScreen#remove after sleeping")
splashScreenView.remove()
}
}
複製代碼
能夠看到啓動畫面展現以後,不做定製的默認狀況下就是全屏一下再消失。
日誌以下:
Splash : Activity:com.example.splash.MainActivity@f70c0d0 Activity:com.example.splash.MainActivity@f70c0d0 onCreate
Splash : Activity:com.example.splash.MainActivity@f70c0d0 onStart
Splash : Activity:com.example.splash.MainActivity@f70c0d0 onResume
Splash : SplashScreen#onSplashScreenExit view:android.window.SplashScreenView{18339d5 V.E...... ........ 0,0-1080,2280}
Splash : SplashScreen#remove after sleeping
複製代碼
必定記得調用remove
及時移除啓動畫面,不然SplashScreen會長時間蓋在主畫面上,大概在5s
左右。
另外,回調的註冊須要放在Activity#onResume
前,否則監聽不到。
能夠給啓動畫面的總體設置TRANSLATE
、SCALE
、ROTATE
、ALPHA
等各類動畫,使得退出更加天然。
好比給SplashScreen加上一個縮小出屏幕的動畫。
private fun customizeSplashScreenExit() {
splashScreen.setOnExitAnimationListener { splashScreenView ->
showSplashExitAnimator(splashScreenView)
}
}
private fun showSplashExitAnimator(splashScreenView: SplashScreenView) {
val path = Path()
path.moveTo(1.0f, 1.0f)
path.lineTo(0f, 0f)
val scaleOut = ObjectAnimator.ofFloat(
splashScreenView,
View.SCALE_X,
View.SCALE_Y,
path
)
...
scaleOut.doOnEnd {
splashScreenView.remove()
}
scaleOut.start()
}
複製代碼
又或者從上方平移出屏幕的動畫。
private fun showSplashExitAnimator(splashScreenView: SplashScreenView) {
val slideUp = ObjectAnimator.ofFloat(
splashScreenView,
View.TRANSLATION_Y,
0f,
-splashScreenView.height.toFloat()
)
...
slideUp.start()
}
複製代碼
固然也能夠給圖標單獨加上動畫,好比將Icon上滑。
private fun customizeSplashScreenExit() {
splashScreen.setOnExitAnimationListener { splashScreenView ->
showSplashIconExitAnimator(splashScreenView)
}
}
private fun showSplashIconExitAnimator(splashScreenView: SplashScreenView) {
val iconView = splashScreenView.iconView ?: return
val slideUp = ObjectAnimator.ofFloat(
splashScreenView.iconView,
View.TRANSLATION_Y,
0f,
-iconView.height * 2.toFloat()
)
...
slideUp.start()
}
複製代碼
針對退出動畫的定製官方還有一段補充說明。
By the start of this callback, the animated vector drawable on the splash screen has begun. Depending on the duration of the app launch, the drawable might be in the middle of its animation. Use
SplashScreenView.getIconAnimationStart
to know when the animation started. You can calculate the remaining duration of the icon animation.
簡言之,退出畫面回調的時候Icon動畫可能進行到了一半,最好計算Icon動畫的剩餘時長來執行退出動畫。
緣由在於設備性能會影響App描畫的遲早,而第一幀描畫的時候上述的退出回調將被執行。也就是說,性能的優劣會影響啓動畫面退出的回調時機。
不能爲了展現效果而讓用戶久等,不然會弄巧成拙。
藉助SplashScreenView的iconAnimationStartMillis
和iconAnimationDurationMillis
方法能夠推算出Icon動畫的剩餘時長。
*模擬器上運行的緣故,大部分時候個人Demo在啓動畫面退出的時候Icon動畫都結束了,少部分狀況下動畫還剩餘一點時間,可能實機的狀況會不同。
private fun showSplashIconExitAnimator(splashScreenView: SplashScreenView) {
slideUp.duration = getRemainingDuration(splashScreenView)
...
}
fun getRemainingDuration(splashScreenView: SplashScreenView): Long {
// 取得Icon動畫的時長
val animationDuration = splashScreenView.iconAnimationDurationMillis
// 取得Icon動畫的開始時刻
val animationStart = splashScreenView.iconAnimationStartMillis
// 再結合當前時間計算出Icon動畫的剩餘時長
// 1. 時長爲負則固定爲0ms即直接退出
// 2. 時長爲正則採用該時長執行退出動畫
return if (animationDuration != null && animationStart != null) {
(animationDuration - SystemClock.uptimeMillis() + animationStart)
.coerceAtLeast(0L)
} else {
0L
}
}
複製代碼
類/接口 | 做用 |
---|---|
SplashScreen | 啓動畫面管理接口,經過Activity#getSplashScreen取得 |
OnExitAnimationListener | 啓動畫面退出的回調接口,經過SplashScreen#setOnExitAnimationListener註冊 |
SplashScreenView | 啓動畫面包含的視圖,用以定製總體或Icon的退出動畫 |
attr | 做用 | 備註 |
---|---|---|
splashScreenTheme | 指定SplashScreen相關的Style | 存在一點問題好比brand圖片會不顯示 |
windowSplashScreenBackground | 定製啓動畫面的背景 | 默認從windowBackground裏讀取 |
windowSplashScreenBrandingImage | 指定啓動畫面底部的品牌圖標 | - |
windowSplashScreenAnimatedIcon | 指定Icon,支持靜態或動畫Drawable | - |
windowSplashScreenAnimationDuration | 指定動畫Icon時長 | 上限1000ms |
windowSplashScreenIconBackgroundColor | 補充Icon背景色 | - |
注意:windowSplashscreenContent
是8.0版本新增的定製啓動畫面的屬性,自12開始廢棄了,使用windowSplashscreenAnimatedIcon
替代
須要嚐鮮SplashScreen的話,須要在Android 12上開發,並作以下必要配置。
compileSdkVersion和targetSdkVersion聲明爲S
android:exported="true"
,明示聲明啓動畫面的可見性,不然會安裝失敗
另外啓動頁的Icon不管是靜態的仍是動畫效果的,都應遵循Adaptive Icon
的規範,否則Icon會發生變形。
Android 12上全新的SplashScreen
API很是簡單清晰,整個定製過程很是流暢!
相信在全新的API加持下,APP的啓動畫面能夠迸發出更多特點的、好玩的創意。
快快嘗試起來,給你的用戶留下第一眼的好印象~