AndroidX SplashScreen:全新App啟動(dòng)頁面

Android 12 添加了 SplashScreen API,它可為所有應(yīng)用啟用新的應(yīng)用啟動(dòng)動(dòng)畫。這包括啟動(dòng)時(shí)的進(jìn)入應(yīng)用運(yùn)動(dòng)、顯示應(yīng)用圖標(biāo)的啟動(dòng)畫面,以及向應(yīng)用本身的過渡。

Android 12 上效果固然不錯(cuò),可如果不兼容低版本系統(tǒng)的話,實(shí)屬雞肋。
AndroidX 推出了一個(gè)叫 SplashScreen 的同名 API,很顯然它就是用來兼容低版本的 SplashScreen 功能庫。

現(xiàn)在 App 大多點(diǎn)擊圖標(biāo)啟動(dòng)后,一般都會(huì)有幾個(gè)固定的界面:

  • Splash 啟動(dòng)頁,展示 logo
  • Advertising 廣告頁,展示開屏廣告、節(jié)日活動(dòng)等
  • Guide 用戶引導(dǎo)頁面,展示重點(diǎn)功能,一般只展示一次
abc.png

如果不適配 SplashScreen,在 Android 12 上,系統(tǒng)默認(rèn)為 App 生成的啟動(dòng)頁,加上 App 自身的啟動(dòng)頁,就會(huì)有兩個(gè)啟動(dòng)頁,顯然不是我們期望的效果。
同時(shí)為了兼容低版本,這時(shí)候就需要引入 SplashScreen 庫:


1.0 引入 SplashScreen 庫

目前最新版本可在 官網(wǎng) 查詢

implementation "androidx.core:core-splashscreen:1.0.0"


1.1 添加樣式

themes.xml:

<style name="Theme.TestSplashScreen.Starting" parent="Theme.SplashScreen">
    # 啟動(dòng)畫面的背景,默認(rèn)使用 windowBackground
    <item name="windowSplashScreenBackground">@color/...</item>
    # 指定 icon,支持靜態(tài) drawable 或動(dòng)畫 vector drawable
    <item name="windowSplashScreenAnimatedIcon">@drawable/...</item>
    # 動(dòng)畫 icon 時(shí)長,上限 1000 ms
    <item name="windowSplashScreenAnimationDuration">1000</item>
    # 啟動(dòng)畫面退出后 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)頁 LauncherActivity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    // 在 setContentView 之前調(diào)用
    val splashScreen = installSplashScreen()
    // 非必須,可刪除
    setContentView(R.layout.activity_launcher) 
    ...
}

這樣最基本的適配就結(jié)束了。


2.2 延長啟動(dòng)頁面

通過 SplashScreen#setKeepOnScreenCondition 延長啟動(dòng)頁

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)頁延長至 2 秒


2.2 定制啟動(dòng)頁退場(chǎng)動(dòng)畫

通過 SplashScreen#setOnExitAnimationListener 設(shè)置動(dòng)畫效果
這里分別設(shè)置了,SplashScreen 頁面和中間 icon 的退場(chǎng)動(dòng)畫

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)畫的時(shí)長可以不一樣,這里監(jiān)聽 splashScreen 動(dòng)畫結(jié)束
        showSplashExitAnimator(provider.view) {
            provider.remove()
            // 進(jìn)入主界面,順便給個(gè) FadeOut 退場(chǎng)動(dòng)畫
            startActivity(Intent(this, MainActivity::class.java))
            finish()
            overridePendingTransition(0, R.anim.fade_out)
        }
    }
}

// splash screen view 退場(chǎng)動(dòng)畫:透明度 + 揭露
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)畫:透明度 + 縮放
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


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容