主頁 > 移動端開發 > 深度探討 Jetpack SplashScreen 如何重塑應用啟影片面

深度探討 Jetpack SplashScreen 如何重塑應用啟影片面

2021-11-08 10:23:01 移動端開發

在這里插入圖片描述

可能有不少小伙伴已經留意到 Android 12 上推出了全新的啟影片面 API SplashScreen,同時為了兼容低版本的使用,Jetpack 框架里推出了同名庫,

本次針對這個庫的使用和原理做個全面的介紹,將按照如下幾個方面去展開:

  • 首先簡單探討一下為什么需要啟影片面
  • 接著一起回顧一下之前打造啟影片面的常規做法
  • 然后重點闡述一下 SplashScreen 庫的目的,以及如何使用
  • 最后介紹一下 SplashScreen 庫大致的實作原理

1. 為什么需要啟影片面

1.1 啟影片面的作用

12-widget
  • 當我們打開各種桌面軟體或移動端 App 的時候,首先會看到的經常是一段過渡畫面,用以展示產品所屬的公司、品牌的圖示、公司的 Slogan 等,借以宣傳公司的調性和傳達一些特色

  • 同時在節日慶典、特殊活動的時候展示廣告頁面或活動海報

  • 在啟影片面展示的同時,背后的內容得以加載

同時在一定程度上緩解用戶的等待,啟影片面良好設計的話,還能使得這個枯燥的程序顯得有趣和充滿期待!

1.2 App 啟動的典型流程

來看一下 App 啟動的典型流程,

點擊 Launcher 上的 Logo 之后,首先用戶將看到一個啟影片面,如果是初次啟動的話接著會展示 Guide 畫面引導用戶了解如何使用;如果是節榷訓廣告需要的話展示的是廣告宣傳畫面,最后才是展示內容的主畫面,

我們本次探討的核心就是第一個啟影片面,

Jetpack SplashScreen 庫正是用來打造用戶看到的第一個啟影片面,在講述之前,先來回顧一下之前的常規做法,

2. 常規做法

我們知道從點擊 Launcher 上的 icon 到 App 內容描畫之前,有很多準備作業,在這段時間是看不到目標 App 內容的,

為了快速回應用戶的點擊或緩解用戶的等待,在 App 描畫之前系統將啟動專用的 SplashScreenWindow 蓋在 App 之上,該Window 的呈現源自于 App 主題方面的配置,

配置的不同進而影響到啟動 Window 的表現,我們來看看各種配置的做法,

2.1 默認的啟影片面背景

假使 App 的主題針對 Window 背景什么都不設定,你會發現啟動的程序中總有個默認的白畫面一閃而過,

 <style name="SplashThemeBase.DefaultBg"/>
12-widget

2.2 不設定啟影片面背景

當然可以將 windowBackground 設定為空,可是你會發現啟動的程序中又變成黑色一片閃過,

<style name="SplashThemeBase.NoBg">
    <item name="android:windowBackground">@null</item>
</style>
12-widget

無論是白畫面還是黑畫面一閃而過,都無法接受,所以還得繼續優化,

2.3 直接關閉啟影片面

無論白畫面還是黑畫面一閃而過的體驗都不好,這時候可能會想到關閉默認的啟影片面,通過 windowDisablePreview 屬性可以徹底關閉啟影片面,

<style name="SplashThemeBase.TransparentBg">
    <item name="android:windowDisablePreview">true</item>
</style>

這樣一來,確實看不到啟影片面的存在了,但整個程序貌似“變慢”了,實際上性能并沒有**“劣化”**,只是啟動中過渡的 Window 不存在了,假使 ApplicationActivity 等組件里存在耗時邏輯的話,這種劣化會更加明顯,

相較于前面的黑白畫面一閃而過,這種變慢的體驗也好不到哪去,

12-widget

2.4 設定啟影片面背景

繞來繞去,最后我們發現還是得提供一個恰當的啟影片面,具體在于將 UI 提供的背景色、Icon、Brand 等資源組合成一個 LayerListDrawable,然后將其設定到 windowBackground 中即可,

<!--theme-->
<style name="SplashThemeBase.WithBg">
    <item name="android:windowBackground">@drawable/ic_splash_bg</item>
</style>

<!--drawable-->
<layer-list ... >
    <item android:drawable="@drawable/ic_logo"></item>
    <item  android:drawable="@drawable/ic_brand"></item>
</layer-list>
12-widget

這種做法既可以提供體驗良好的過渡畫面,同時可以快速回應用戶的點擊,可以說是最為簡單也最為便捷的一種實作方式,

2.5 設定啟影片面內容

Android 8 加入一個配置啟影片面的屬性 windowSplashscreenContent,用于配置啟影片面的內容,它的優先級高于 windowBackground,兩者一起設定的話呢,會覆寫在 windowBackground 上,大體的效果呢和前面的差不多,不再贅述,

<style name="SplashThemeBase.WithBg.SplashScreenContent">
    <item name="android:windowSplashscreenContent">@drawable/ic_splash_content</item>
</style>

注意:這個屬性自 Android 8 加入,從 12 開始廢棄

2.6 定義啟動專用 Activity

有的時候覺得單單設定一張背景圖太過簡單,無法實作一些復雜的啟動效果,又或者希望能和廣告或節日的畫面深度配合起來,這時候難免需要定義一個專門的啟動 Activity

具體來講需要在入口 Activity 前預設專門的 Splash Activity來顯示啟影片面,Splash Activity 在一定時間后自動跳轉到入口 Activity

啟動 Activity 提供類似啟影片面的布局,

<androidx.constraintlayout.widget.ConstraintLayout ... >
    <ImageView android:src="@drawable/ic_icon" ...  />
    <ImageView android:src="@drawable/ic_brand" ...  />
</androidx.constraintlayout.widget.ConstraintLayout>

在畫面展示后配合定時跳轉等邏輯去打開廣告或海報等畫面,

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContentView(binding.root)
        goToMainScreenDelayed()
    }
}

因為有了自己實作的啟動 Activity,所以系統提供的啟影片面可以關閉,當然也可以將系統的啟影片面的 windowBackground 設定為整體背景色相接近的色調,

<style name="SplashThemeBase.NoSplashWindow" >
    <item name="android:windowDisablePreview">true</item>
</style>
12-widget

上面是通過 Activity 方式打造的啟影片面的效果,

DEMO 的效果和前面的主題方式的效果差不多,但是這種做法將會擁有更大的定制空間,比如可以接入跨平臺的 Lottie 影片庫,打造更為豐富、炫酷、平臺統一的啟動效果!

3. Jetpack SplashScreen 的目的和使用

可以看到之前提供的主題配置方式只能展示一張背景圖,這種靜態效果看久了著實單調,而且這張圖還需要我們自行組合 App 的 Icon、Brand 的 Logo,這就需要考慮很多 Size、位置、螢屏適配等 UI 細節,不太方便,如果需要豐富、靈活一些的效果,又需要自定義專用的 Activity 來解決,這樣一來又顯得繁瑣,

正因為此,Jetpack 上推出了 SplashScreen 庫,來高效、靈活地重塑啟影片面,而 SplashScreen 庫基于 Android 12 的新特性 SplashScreen API,所以我們先來了解一下這個特性,

3.1 Android 12 SplashScreen

3.1.1 特性介紹

Android 12 上 App 啟動的時候將先展示一個啟影片面的進場部分,該部分的呈現由 SplashScreen API 提供的更為豐富的屬性來定制,之后在App 描畫第一幀的時候將該畫面的視圖回呼給我們來定制退場部分的效果,

進場和退場兩部分共同組成了全新的 SplashScreen API,
在這里插入圖片描述

需要說明的是,不同于之前的版本,12 上什么即便都不配置,啟影片面會是白色背景下展示 Adaptive Icon 的一個效果,這相較于之前的純白背景要好了不少,

3.1.2 畫面構成

SplashScreen 的畫面由一個 SplashScreenWindow 承載,中間是一個 AdaptiveIcon,SplashScreen API 則提供了豐富的屬性來指定該的各部分構成,簡潔明了,比如:

  • windowSplashScreenAnimatedIcon:指定 App Icon 的圖片,如果指定的是 Animated Vector Drawable 的話,在啟影片面展示的同時會執行一個 Icon 影片效果
  • windowSplashScreenIconBackgroundColor:Icon 背景色
  • Adaptive Icon Mask:由 Launcher 的配置指定,主題無法更改,說明一下
  • windowSplashScreenBackground:Window 背景色,和之前的 windowBackground 屬性類似
  • windowSplashScreenBrandingImage:放置品牌 Logo

當 SplashScreenWindow 退場的時候整個畫面的視圖會被封裝到 SplashScreenView 中供我們實作退場效果,

下面這是 Gmail 適配了 SplashScreen API 后的啟動效果,簡單來講就是一個進場的 M 字母的 Path影片,退場的時候直接結束到目標內容的效果,

并沒有適配 Icon 背景、Brand Logo、退場影片等特性,后面的章節將充分演示各項特性,

12-widget

3.1.3 啟動規則

在冷啟動和暖啟動的時候展示 SplashScreen,熱啟動的時候從不展示該畫面,

  • 冷啟動:行程尚不存在的情況下點擊 App Icon 的啟動
  • 暖啟動:行程存在但 App 不在前臺,并且 Activity 已經銷毀了的啟動
  • 熱啟動:行程存在但 App 不在前臺,并且 Activity 尚未銷毀的啟動

3.2 Jetpack SplashScreen

3.2.1 目的

Android 12 的 SplashScreen API 非常好用,但是低版本無法嘗鮮,所以 Jetpack 里推出了同名庫,

它是為了兼容 12 之前低版本的 UI 庫,最早兼容到 Android 6(API 23),6.0 及以上的設備占用率近 9 成,完全夠用了,它通過整合和統一 SplashScreen API,達到一套代碼實作近乎一致的 App 啟動體驗,

簡述:

  • 兼容低版本系統的啟影片面 UI 庫
  • 支持到 Android 6.0(近 9 成)
  • 整合 12 的 SplashScreen API
  • 一套代碼打造近乎一致的啟動效果

3.2.2 提供的主題和屬性

SplashScreen 庫的版本到了 1.0.0-alpha01 版,在 gradle 檔案里簡單匯入即可,

dependencies {
    implementation "androidx.core:core-splashscreen:1.0.0-alpha01"
}

它預設了主題供我們使用,必須將入口 Activity 的主題改成擴展自這個主題,

<style name="Theme.SplashScreen" parent="Theme.SplashScreenBase">
    <item name="windowSplashScreenAnimatedIcon">@android:drawable/sym_def_app_icon</item>
    ...
</style>

同時預設了些屬性供我們覆寫和提供自己的啟影片面資源,

<attr format="reference" name="postSplashScreenTheme"/>
<attr format="reference" name="windowSplashScreenAnimatedIcon"/>
<attr format="integer" name="windowSplashScreenAnimationDuration"/>
<attr format="color" name="windowSplashScreenBackground"/>

這里需要提醒兩點:

  1. SplashScreen 庫針對 12 之前的低版本暫不支持設定 Icon Bg,和 Brand Icon 的屬性
  2. postSplashScreenTheme 屬性用來指定 Activity 最終的主題,避免使用預設的主題影響目標 Actvitiy 的主題展示

3.2.3 提供的 API

SplashScreen 庫還提供了邏輯方面的 API,供我們去實作啟影片面時長的控制,啟影片面退場效果的實作需求,

API說明
SplashScreenJetpack版獲取定制啟影片面入口的類
Activity#installSplashScreen()覆寫Activity,用以獲取定制入口的靜態成員函式
setKeepVisibleCondition指定保持啟影片面展示的條件
KeepOnScreenCondition實作展示條件的介面
setOnExitAnimationListener監聽啟影片面的退出時機
OnExitAnimationListener啟影片面退出的回呼介面
SplashScreenViewProvider定制退場效果的啟影片面視圖

3.3 SplashScreen 庫的使用

3.3.1 打造進場效果

進場效果的部分只涉及到資源方面的配置,

首先擴展自 SplashScreen 庫的預設主題作成一個 Base 主題,指定諸如啟影片面背景、目標 Activity 背景等方面的共同屬性,

<style name="SplashScreenTheme.Base" parent="Theme.SplashScreen">
    ...
    <item name="windowSplashScreenBackground">@color/splashBackground</item>
    <item name="postSplashScreenTheme">@style/TargetScreenTheme</item>
</style>

其他的一些屬性需要針對低版本和 12 作區分,比如 12上可以指定其獨有的影片 Icon,Icon 背景和 Brand logo,而低版本上指定一個靜態 Icon 即可,但為了效果接近,可以指定 App 的 Adaptive Icon,

<!-- version:12- -->
<style name="SplashScreenTheme" parent="SplashScreenTheme.Base">
    <item name="windowSplashScreenAnimatedIcon">@mipmap/ic_icon_adaptive</item>
</style>

<!-- version:12+ -->
<style name="SplashScreenTheme" parent="SplashScreenTheme.Base">
    <item name="windowSplashScreenAnimatedIcon">@drawable/ic_icon_animated</item>
    <item name="android:windowSplashScreenIconBackgroundColor">@color/iconBackground</item>
    <item name="android:windowSplashScreenBrandingImage">@drawable/ic_brand</item>
</style>

我們來看一下分別運行在 Android 8 和 12 上的進場效果:

在這里插入圖片描述

可以看到高低版本上是比較接近的進場畫面,只不過 12 上多了特有的 Kotlin 的組合影片和一個 TechMerger 字樣的 Brand Logo,

3.3.2 延長啟影片面

隨著 App 第一幀的開始描畫,SplashScreenWindow 即將消失,如果背面的業務邏輯尚未準備完畢,那體驗不是很好,魚骨屏什么的就是用來優化這個問題,

當然對于啟影片面來講,現在可以通過 SplashScreen 庫的 API 來靈活控制啟影片面的時長,確保內容好了再退出,這個 API 就是 installSplashScreen()

通過這個靜態函式可以拿到定制的入口,之后可以呼叫 setKeepVisibleCondition() 設定啟影片面保持展示的條件,條件可以 ViewModel 的耗時加載相結合,這里提供的是一個 ViewModel 實體初始化 2s 之后再退出啟影片面的一個模擬邏輯,

注意:由于 installSplashScreen 函式內部將呼叫 setTheme 反映實際的主題,所以需要在 setContentView 之前呼叫

class JetpackSplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        splashScreen = installSplashScreen()
        setContentView(binding.root)
        splashScreen.setKeepVisibleCondition {
            !viewModel.isDataReady() 
        }
    }
}

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
}

我們來看一下分別運行在 Android 8 和 12 上啟影片面的延長效果:
在這里插入圖片描述

可以看到高低版本上都成功實作了啟影片面的延遲退出,

3.3.3 打造整體退場效果

當啟影片面退出的時候如果能提供一個無縫過渡到目標內容的影片,體驗會更好,我們可以利用 SplashScreen 庫的 setOnExitAnimationListener 來針對進場畫面的整體視圖實作一個退場的影片,

比如這里我們定制一個 SplashScreen 整體的下移淡出效果,

注意:記得在影片結束的時候呼叫 SplashScreenViewProvider 的 remove() 及時將啟影片面的視圖移除,否則可能覆寫在實際畫面上,遮擋內容,

class JetpackSplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        splashScreen.setOnExitAnimationListener { splashScreenViewProvider ->
            showSplashExitAnimator(splashScreenViewProvider.view) {
                splashScreenViewProvider.remove()
            }
        }

    private fun showSplashExitAnimator(splashScreenView: View, onExit: () -> Unit = {}) {
        ...
        AnimatorSet().run {
            ...
            playTogether(slideDown, alphaOut)
            doOnEnd { onExit() }
        }
    }
}

我們來看一下分別運行在 Android 8 和 12 上的退場效果:

在這里插入圖片描述

可以看到 12 上的 Brand Logo 是一起執行的退場影片,總的來說高低版本上都實作了幾乎一致的整體下移和淡出的效果,

3.3.4 打造 Icon 獨有的退場效果

如果覺得整體的退場影片太過突兀或夸張,還可以針對 App Icon 作單獨的退場效果,基本邏輯和定制整體的退場效果差不多,,區別在于執行影片的物件由 view 變成了 IconView

同樣要注意在影片結束的時候呼叫 remove()

class JetpackSplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        splashScreen.setOnExitAnimationListener { splashScreenViewProvider ->
            showSplashIconExitAnimator(splashScreenViewProvider.iconView) {
                splashScreenViewProvider.remove()
            }
        }

    private fun showSplashIconExitAnimator(iconView: View, onExit: () -> Unit = {}) {
        ...
        AnimatorSet().run {
            ...
            playTogether(alphaOut, scaleOut, slideUp)
            doOnEnd { onExit() }
        }
    }
}

這里延時的是針對 Icon 的上移和淡出影片,來看一下效果:

在這里插入圖片描述

可以看到 12 上的 Brand Logo 是不動的,整體上都是一個 Icon 上移和淡出的效果,

3.3.5 控制退場影片的時長

設備性能或狀態會影響 App 開始描畫的時間,為了讓用戶早點看到實際內容,可以靈活控制退場影片的時長,比如當描畫得晚,可以考慮不展示退場影片或執行極短的固定時長;當描畫得早,進場影片可能尚未結束,將剩余的時長交接給退場部分,

主要通過 SplashScreen 庫回傳的進場影片開始時刻(iconAnimationStartMillis)和總時長(iconAnimationDurationMillis)的 API,與退場回呼的當前時刻進行計算即可,

需要注意的是,針對 12 之前的版本,SplashScreen 庫的進場部分不支持 Icon 影片,所以上述的兩個屬性總是回傳 0,需要特別處理一下,

private fun getRemainingDuration(provider: SplashScreenViewProvider): Long {
    val animationDuration = provider.iconAnimationDurationMillis
    val animationStart = provider.iconAnimationStartMillis

    return if (animationDuration == 0L || animationStart == 0L)
        defaultExitDuration
    else (animationDuration - SystemClock.uptimeMillis() + animationStart)
        .coerceAtLeast(0L)
}

3.4 Lottie 支持 SplashScreen 嗎?

Lottie 是跨平臺的影片庫,非常好用,那么 SplashScreen 庫支持嗎?

并不支持,因為 SplashScreen 庫只能配置 Drawable 檔案,不可以替換 View,而 Lottie 的效果完全依賴于自定義的 AnimationView

但 SplashScreen 的設計者提供了一個魔改思路:

  1. 拷貝 Lottie Json 的第一幀,做成 SVG 并轉換為 Animated Vetor Drawable,設定到 Splash Icon
  2. 目標布局正中放入執行 Icon 影片的 View
  3. 在 Splash 退出的時候將 LottieAnimationView 決議 Json 并執行影片
  4. 影片結束后記得將真正的視圖展示

代碼示例:

splashScreen.setOnExitAnimationListener { vp ->
    val lottieView = findViewById<LottieAnimationView>(R.id.animationView)
    ...
    lottieView.postDelayed({
        vp.view.alpha = 0f
        vp.iconView.alpha = 0f
        lottieView!!.playAnimation()
    }, delay)

    lottieView.addAnimatorListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator?) {
            imageView.visibility = View.VISIBLE
        }
    })
}

我們來看一下高低版本上魔改之后支持 Lottie 的 SplashScreen 效果,可以看到是幾乎完全一致的非常流暢、絲滑的啟動體驗,
在這里插入圖片描述

但這種做法違背了 SplashScreen 庫的設計初衷,不建議使用,這里只是提供一種思路,

更詳細的說明可以參考官方的 DEMO 介紹:https://github.com/vcaen/splashscreen-sample

4. SplashScreen 庫的實作原理

接下來了解一下 SplashScreen 庫如何兼容低版本,實作幾乎一致的啟動效果,

4.1 總體原理

寫這個資料的時候 Android 12 的原始碼尚未公開,最近公開了之后看了一眼,發現 SplashScreen 的實作非常繁雜,

這里簡單提一下關鍵地方,SplashScreenWindow 退出的時候,系統會通過 AIDL 將封裝了啟影片面的資訊的序列化物件傳遞給 App 行程,App 將物件反序列化并創建退場視圖即 SplashScreenView,然后添加到 DecorView 上去,之后 App 即可對這個視圖作退場效果的定制,

更多全面的細節,感興趣的朋友可自行研究,本次主要Jetpack SplashScreen 庫的原始碼進行解讀,

總體的原理分為進場和退場兩個部分,

進場部分的畫面針對 12 之前的版本是 windowBackground 思路,針對 12 是系統專屬的 SplashScreen 系屬性實作的,

退場部分,在 12 之前是自定義的 FrameLayout 添加到 Activity 的 ContentView 上,12 則是反序列化的 SplashScreenView 添加到了 DecorView 上,

4.2 進場畫面的原理

進場畫面的原理完全依賴于主題的配置,面向低版本的話和之前的常規做法是一樣的思路,即提供一個讀取我們配置的畫面資源的 LayerListDrawable 放置到 windowBackground 中,

<style name="Theme.SplashScreen" parent="Theme.SplashScreenBase">
    ...
</style>

<style name="Theme.SplashScreenBase" parent="android:Theme.NoTitleBar">
    <item name="android:windowBackground">@drawable/compat_splash_screen</item>
    ...
</style>
<!--compat_splash_screen.xml-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:gravity="fill">
        <color android:color="?attr/windowSplashScreenBackground" />
    </item>
    <item
        android:drawable="?attr/windowSplashScreenAnimatedIcon"
        ... />
</layer-list>

面向 12 的話,則是由系統回呼專屬的屬性去構建啟影片面的視圖,

<style name="Theme.SplashScreen" parent="android:Theme.DeviceDefault.NoActionBar">
    <item name="android:windowSplashScreenAnimatedIcon">?windowSplashScreenAnimatedIcon</item>
    <item name="android:windowSplashScreenBackground">?windowSplashScreenBackground</item>
    ...
</style>

4.3 定制入口的初始化

獲取 SplashScreen 實體的 API 是 installSplashScreen(),其運行在低版本上的話,需要額外讀取和快取 Icon 和 Background 的配置,然后讀取并設定目標 Activity 的 Theme,12 上的話,Window 背景,Icon 和 Branding 等屬性由系統控制,只需要配置目標 Activity 的 Theme 即可,

4.4 延長啟影片面

通過 setKeepVisibleCondition() 可以延長啟影片面的展示,無關運行的版本,原理都是向 ContentView 的 ViewTreeObserver 注冊OnPreDrawListener回呼來實作,

系統在描畫前先回呼 onPreDraw(),獲取是否放行描畫的條件,此處將回呼 KeepVisibleCondition 的邏輯,如果不放行,在下次螢屏重繪的時候繼續回呼,直到滿足條件開始描畫,啟動 Window 消失,描畫放行的時候,低版本額外需要手動呼叫 dispatchOnExitAnimation 來執行退出回呼,12 則由系統自行執行,

需要注意:這個時候退場用的自定義視圖仍然還沒添加上來,只是延遲了 SplashScreenWindow 的退出而已,

4.5 退場畫面的回呼

setOnExitAnimationListener() 可以監聽退場時機,

運行在低版本上的時候,需要手動加載啟影片面的布局到 ContentView中,并將之前設定的 Window Background 和 Icon 等屬性顯示,然后添加 Layout change 回呼,在布局完畢的時候通過 adjustInsets 特殊處理將 Icon 位置調整一下,判斷啟影片面保持條件是否達到,達到的話呼叫 onSplashScreenExit

運行在 12 上的時候,布局不需要手動準備,通過 12 專用的系統介面,將視圖快取到 Provider 里即可,后續的 Exit 也由系統執行,

在這里插入圖片描述

4.6 adjustInsets 的特殊處理

面向低版本的退場畫面在布局成功后會呼叫的特殊處理,

進場部分的是 Window Drawable,Icon 是居中的,但退場部分是向 ContentView中手動添加的 Framelayout 布局,Icon 在布局里是居中的,但由于 StatusBarNavigationBar 高度不一樣,Icon 在整個 Window 里是偏下的,

如果不加干預的話,進場過渡到退場的時候,Icon 會發生跳躍,
在這里插入圖片描述

原始碼通過 windowInsets API 獲取狀態欄和導航欄的高度,取差值的一半交由 IconView 去移動到 window 中間,

private class Impl23(activity: Activity) : Impl(activity) {
    override fun adjustInsets( ... ) {
        // Offset the icon if the insets have changed
        val rootWindowInsets = view.rootWindowInsets
        val ty = rootWindowInsets.systemWindowInsetTop
                - rootWindowInsets.systemWindowInsetBottom
        splashScreenViewProvider.iconView.translationY = -ty.toFloat() / 2f
    }
}

結語

到這里我們探討了啟影片面的必要性、回顧了啟影片面打造的常規做法、介紹了 Android 12 上 SplashScreen API、以及詳細了解了 Jetpack SplashScreen 庫的目的、使用細節和實作原理

可以看到 SplashScreen 庫簡單又清晰,可以幫助我們靈活、高效地重塑啟影片面,主要體現在這么幾個方面:

  • 配置圖示影片、圖示背景、品牌 Logo 等新元素,打造豐富的進場效果

  • 適當地調節啟影片面的展示時間,以配合后臺的加載

  • 針對整體或 Icon 視圖,靈活打造無縫銜接的退場效果

  • 靈活控制退場影片的有無和時長,自然地過渡到目標內容

值得提醒的是:啟影片面只是過渡,影片效果避免突兀,更不要過多占用用戶時間!

參考資料&資源分享

主要分享一下使用到的參考資料以及分享不錯的 DEMO,大家可以通過這些資料和 DEMO 切實感受和實踐下 SplashScreen 庫的玩法!

名稱地址
Splash Screen API 的官方介紹https://developer.android.google.cn/about/versions/12/features/splash-screen
Jetpack SplashScreen 庫的地址https://developer.android.google.cn/jetpack/androidx/releases/core?hl=zh-cn#core_splashscreen_version_100_2
本文原始碼公開后的 SplashScreen 專案https://github.com/ellisonchan/SplashScreen
ComposeBird 專案https://github.com/ellisonchan/ComposeBird

最后一個著重說一下,這是我之前采用 Jetpack Compose 寫的 Flappy Bird 小游戲,我抽空給它適配了 SplashScreen 庫的功能,可以看到游戲啟動的時候小鳥漸漸飛進來,之后小鳥向上淡出到游戲界面的效果,

12-widget

FAQ

問題回答
1. 不知道有沒有解決多語言適配的問題 多螢屏適配問題不顯示文字,不存在語言問題;Icon 按照 Adaptive Icon 規格提供即可,尺寸及居中的問題,SplashWindow 功能會處理
2. 這樣等于固定了樣式,請問產品能答應嗎,到頭來還不如就設定一張圖片的好,感覺沒有用武之地,而且ios沒有這玩意,無法同步,注定是個雞肋這個 API 在設定靜態背景之外可以設定影片圖示,品牌圖示,退場效果等,可以給需要豐富啟動效果的 App 提供便利,iOS 的話我想肯定有同樣的辦法做到,只不過沒 Android 方便,
3. 了解確實可以,但這東西在國內似乎沒啥用?國內啥 APP不放點廣告,有能耐的自己做廣告宣傳 沒能耐的接穿山甲,接優量匯,其實不影響廣告界面的展示,因為一般廣告展示前還有個展示 Logo 的啟影片面,可以把這個畫面用 SplashScreen 實作,
4. 站在用戶的角度也站在我急躁性格的我,在用一個 app的時侯我一秒了也不想看到Splash頁面的存面.是的,SplashScreen 不能占用過長的時間,所以一般 Icon 影片控制在 1s 內,而退場影片的時長可以盡量縮短,或者榷訓退場效果,
5. SplashScreen和目前設定啟動背景有什么差異啊,是否就是比現在的多了可以設定啟影片面的影片,除了背景還可以設定icon進場影片和品牌logo,除此之外可以便捷地控制啟影片面的時長,還可以定制退場影片,總結起來講,比以前定制的空間更大,但API并不復雜,
6. 如果描畫的時候進場影片沒有執行完畢就消失,體驗會不會不好?描畫很快第一幀很早的話,確實可能發生進場影片進行程序中突然消失的現象,
但進場影片的時長最長只能設定為 1s,第一幀開始描畫且 1s 影片還沒執行完畢的情況很少發生,
一旦發生的話,可以考慮將剩余時間交給退場影片來優化一下,這樣子能夠過渡一下再退出,而不是直接消失,
7. 如果延長條件一直保持會怎么樣?啟動影片會怎么樣?第一幀的描畫一直被掛起,啟影片面 Window 一直殘留,保持著在進場影片最后一幀,無法消失,
8. Activity 指定的 Theme 和 postSplashScreenTheme 的區別是?前者專門為展示啟影片面準備的,后者則是 Activity 的最終主題,目的避免使用啟影片面主題影響到實際的顯示,
9. installSplashScreen 的準確作用?不呼叫的話會怎么樣?installSplashScreen 只是初始化一下定制入口,以及獲取設定資源和設定主題,看不到退場效果,
如果不呼叫的話,仍然可以看到進場畫面,但無法定制退場效果和更新 Activity 主題,
10. 為什么 SplashScreen 庫不設計成低版本也支持 Brand Icon 和 影片 Icon?Brand Icon 不像 App Icon,尺寸和形狀不一,利用現有的 Background Drawable 擔心有尺寸和顯示的問題,Icon 影片需要拿到影片 Drawable 并手動執行,單單利用 WindowBackground 屬性無法做到支持,
11. App 開始 draw 的時候啟動 Window 便消失,那之后的屬性影片為什么還可以執行?12 的時候雖然 Window 退出了,但內部的視圖傳遞了過來放在我們的 DecorView 上,而 12 之前則是自定義的 FrameLayout 添加到了我們的 ContentView 上,
退場的時候我們執行影片的 View 本質上都是我們自己的 View,所以即便 SplashScreenWindow 消失了也沒關系,
12. 為什么 Android 12 是將視圖添加到 DecorView,而 Jetpack 是添加到 ContentView?覆寫到 DecorView 上去,會和系統欄的顯示產生沖突,猜測是 Jetpack 無法解決一些諸如 StatusBar,NavigationBar 的顯示問題,而 Android 12 是系統級別的,可以更高權限地解決這些細節,
13. SplashScreen 功能和 Activity 的 transition 影片的關系?transition 包括 windowIsTranslucent,是針對 Activity window 而言的,和 SplashScreen 的啟影片面專用屬性是不一樣的,應用了 SplashScreen 特性的話,入口 Actvitiy 的影片就不需要了,
14. 退場影片支持共享元素影片嗎?SplashScreenView 不支持設定共享元素在畫面之間的 Transition 影片,
15. 圖示影片的要求?將 SVG 轉成 AnimatedVectorDrawable 的檔案即可,內部通過屬性影片的標簽實作諸如 Path、位移、透明度等影片效果,

推薦閱讀

之前針對 Android 12 的 SplashScreen API 和 Jetpack SplashScreen 庫分別寫過更為詳細的解讀文章,感興趣的朋友可補充食用~

  • Android 12上全新的應用啟影片面,還不適配一下?
  • Jetpack新成員SplashScreen:打造全新的App啟影片面

同時在 GDG 社區說 活動上也分享過本文的話題,可以到 B站 上觀看視頻回放:

「社區說」《使用 Jetpack SplashScreen 打造全新的應用啟動效果》

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/352262.html

標籤:其他

上一篇:KMM(一) 搭建環境,并運行安卓和ios

下一篇:【Android 逆向】Android 逆向通用工具開發 ( 靜態庫專案中的網路操作核心類 CNetwork 分析 )

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more