Android 12 添加了
SplashScreen
API侠姑,它可為所有應(yīng)用啟用新的應(yīng)用啟動(dòng)動(dòng)畫(huà)。這包括啟動(dòng)時(shí)的進(jìn)入應(yīng)用運(yùn)動(dòng)箩做、顯示應(yīng)用圖標(biāo)的啟動(dòng)畫(huà)面莽红,以及向應(yīng)用本身的過(guò)渡。
Android 12 上效果固然不錯(cuò)邦邦,可如果不兼容低版本系統(tǒng)的話安吁,實(shí)屬雞肋。
AndroidX 推出了一個(gè)叫 SplashScreen 的同名 API燃辖,很顯然它就是用來(lái)兼容低版本的 SplashScreen 功能庫(kù)鬼店。
現(xiàn)在 App 大多點(diǎn)擊圖標(biāo)啟動(dòng)后,一般都會(huì)有幾個(gè)固定的界面:
- Splash 啟動(dòng)頁(yè)黔龟,展示 logo
- Advertising 廣告頁(yè)妇智,展示開(kāi)屏廣告、節(jié)日活動(dòng)等
- Guide 用戶引導(dǎo)頁(yè)面氏身,展示重點(diǎn)功能巍棱,一般只展示一次
如果不適配 SplashScreen,在 Android 12 上蛋欣,系統(tǒng)默認(rèn)為 App 生成的啟動(dòng)頁(yè)航徙,加上 App 自身的啟動(dòng)頁(yè),就會(huì)有兩個(gè)啟動(dòng)頁(yè)陷虎,顯然不是我們期望的效果到踏。
同時(shí)為了兼容低版本,這時(shí)候就需要引入 SplashScreen 庫(kù):
1.0 引入 SplashScreen 庫(kù)
目前最新版本可在 官網(wǎng) 查詢
implementation "androidx.core:core-splashscreen:1.0.0"
1.1 添加樣式
themes.xml:
<style name="Theme.TestSplashScreen.Starting" parent="Theme.SplashScreen">
# 啟動(dòng)畫(huà)面的背景尚猿,默認(rèn)使用 windowBackground
<item name="windowSplashScreenBackground">@color/...</item>
# 指定 icon夭禽,支持靜態(tài) drawable 或動(dòng)畫(huà) vector drawable
<item name="windowSplashScreenAnimatedIcon">@drawable/...</item>
# 動(dòng)畫(huà) icon 時(shí)長(zhǎng),上限 1000 ms
<item name="windowSplashScreenAnimationDuration">1000</item>
# 啟動(dòng)畫(huà)面退出后 Activity 的主題
<item name="postSplashScreenTheme">@style/Theme.TestSplashScreen.Launcher</item>
</style>
<style name="Theme.TestSplashScreen.Launcher">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
AndroidManifest.xml:
<activity
android:name=".LauncherActivity"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/Theme.TestSplashScreen.Starting">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
1.2 修改現(xiàn)有的啟動(dòng)頁(yè) LauncherActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在 setContentView 之前調(diào)用
val splashScreen = installSplashScreen()
// 非必須谊路,可刪除
setContentView(R.layout.activity_launcher)
...
}
這樣最基本的適配就結(jié)束了讹躯。
2.2 延長(zhǎng)啟動(dòng)頁(yè)面
通過(guò) SplashScreen#setKeepOnScreenCondition
延長(zhǎng)啟動(dòng)頁(yè)
private var mKeepOnScreen = AtomicBoolean(true)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// need to be called before setContentView.
val splashScreen = installSplashScreen()
lifecycleScope.launch {
delay(2000)
mKeepOnScreen.compareAndSet(true, false)
}
// keep splash screen showing till mKeepOnScreen is false
splashScreen.setKeepOnScreenCondition { mKeepOnScreen.get() }
}
這樣,啟動(dòng)頁(yè)延長(zhǎng)至 2 秒
2.2 定制啟動(dòng)頁(yè)退場(chǎng)動(dòng)畫(huà)
通過(guò) SplashScreen#setOnExitAnimationListener
設(shè)置動(dòng)畫(huà)效果
這里分別設(shè)置了缠劝,SplashScreen 頁(yè)面和中間 icon 的退場(chǎng)動(dòng)畫(huà)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val splashScreen = installSplashScreen()
setContentView(R.layout.activity_launcher)
splashScreen.setOnExitAnimationListener { provider ->
showSplashIconExitAnimator(provider.iconView)
// 兩個(gè)動(dòng)畫(huà)的時(shí)長(zhǎng)可以不一樣潮梯,這里監(jiān)聽(tīng) splashScreen 動(dòng)畫(huà)結(jié)束
showSplashExitAnimator(provider.view) {
provider.remove()
// 進(jìn)入主界面,順便給個(gè) FadeOut 退場(chǎng)動(dòng)畫(huà)
startActivity(Intent(this, MainActivity::class.java))
finish()
overridePendingTransition(0, R.anim.fade_out)
}
}
}
// splash screen view 退場(chǎng)動(dòng)畫(huà):透明度 + 揭露
private fun showSplashExitAnimator(view: View, onExit: () -> Unit) {
val alphaOut = ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0.2f)
val cx = view.width / 2
val cy = view.height / 2
// get the start radius for the clipping circle
val startRadius = hypot(cx.toDouble(), cy.toDouble()).toFloat()
val revealOut = ViewAnimationUtils.createCircularReveal(
view, cx, cy, startRadius, 0f
)
AnimatorSet().apply {
duration = 300
interpolator = DecelerateInterpolator()
playTogether(alphaOut, revealOut)
doOnEnd { onExit() }
}.also { it.start() }
}
// splash icon 退場(chǎng)動(dòng)畫(huà):透明度 + 縮放
private fun showSplashIconExitAnimator(view: View) {
val alphaOut = ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0.2f)
val scaleOut = ObjectAnimator.ofFloat(
view,
View.SCALE_X,
View.SCALE_Y,
Path().apply {
moveTo(1f, 1f)
lineTo(0.2f, 0.2f)
}
)
AnimatorSet().apply {
duration = 300
interpolator = DecelerateInterpolator()
playTogether(alphaOut, scaleOut)
}.also { it.start() }
}
其他
fade_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="200"
android:fromAlpha="1"
android:toAlpha="0" />
</set>
參考:
https://developer.android.com/guide/topics/ui/splash-screen/migrate
https://developer.android.com/about/versions/12/features/splash-screen