啟動畫面

Android 12 新增了 SplashScreen API,當在搭載 Android 12 及以上版本的裝置中執行時,會為所有應用程式啟用新的應用程式啟動動畫。這包括啟動時的應用程式內建動態、顯示應用程式圖示的啟動畫面,以及應用程式自身的轉換。

啟動畫面樣本
圖 1:啟動畫面樣本

新版服務每次推出應用程式時,都會提供標準設計元素,但您也可以自訂應用程式,讓應用程式保有獨特的品牌宣傳元素。

除了直接使用 SplashScreen API 之外,您亦可使用 SplashScreen相容性程式庫,其中也包含了 SplashScreen API。

啟動畫面的運作方式

當使用者在應用程式執行程序未執行時啟動應用程式(冷啟動)或活動尚未建立(暖啟動)時會發生下列事件。(熱啟動期間一律不會顯示啟動畫面)。

  1. 系統會根據你定義的主題和任何動畫來顯示啟動畫面。

  2. 應用程式就緒後,即可關閉啟動畫面並顯示應用程式。

動畫的元素與機制

動畫元素是由 Android 資訊清單中的 XML 資源檔案定義。每種元素都有淺色和深色模式版本。

包括視窗背景、動畫應用程式圖示及圖示背景:

啟動畫面的元素
圖 2:啟動畫面的可自訂元素

請留意這些元素的以下注意事項:

  • 應用程式圖示 (1) 應該是一個向量可繪項目,可以是靜態或者是動畫。雖然動畫時長不限,但建議不超過 1,000 毫秒。根據預設,使用了啟動器圖示。

  • 圖示背景 (2) 是可選項,如果圖示和視窗背景之間需要更高的對比度,則此選項就能派上用場。如果使用自動調整圖示,當視窗背景的對比度夠高時,就會顯示其背景。

  • 如同自動調整圖示,有三分之一的前景會被遮蓋 (3)。

  • 視窗背景 (4) 包含單一不透明顏色。如果已設定視窗背景且為純色,當未設定屬性時,則根據預設使用此背景。

啟動畫面尺寸

啟動畫面圖示使用的規格與自動調整圖示相同,如下所示:

  • 品牌圖片:大小應為 200×80 dp。
  • 含有圖示背景的應用程式圖示:大小應為 240×240 dp,且符合直徑為 160 dp 的圓環。
  • 不含圖示背景的應用程式圖示:大小應為 288×288 dp,且符合直徑為 192 dp 的圓環。

舉例來說,如果圖片的原尺寸為 300×300 dp,圖示應符合直徑 200 dp 的圓環。圓環以外的所有內容都會被隱藏(遮蓋)。

啟動畫面的元素
圖 3:分別針對實心和透明背景設計了啟動畫面圖示尺寸

啟動畫面動畫

啟動畫面動畫機制包含進入結束動畫。

  • 進入動畫包含啟動畫面的檢視畫面。它由系統控制,無法自訂。

  • 結束動畫由隱藏啟動畫面的動畫放送組成。如需自訂,必須有 SplashScreenView 及其圖示的存取權限,並可在其中放送任意動畫、設定其變形、不透明度和顏色。如果發生這種情況,必須在動畫結束後手動移除啟動畫面。

自訂應用程式中的啟動畫面

根據預設,如果是單色及啟動器圖示,則 SplashScreen 使用 windowBackground 主題。在應用程式主題中新增屬性,才能自訂啟動畫面。

可以透過下列任一方式自訂應用程式的啟動畫面:

  • 設定主題屬性以變更外觀

  • 延長在螢幕上的停留時間

  • 自訂用於關閉啟動畫面的動畫

為啟動畫面設定一個主題以變更外觀

你可以在「活動」主題中指定下列屬性,藉此自訂應用程式的啟動畫面。如果您繼續使用舊版啟動畫面 (使用 android:windowBackground 等屬性),請考慮提供 Android 12 及以上版本的替代資源檔案。

  1. 使用 windowSplashScreenBackground 在背景中填入特定單一顏色:

    <item name="android:windowSplashScreenBackground">@color/...</item>
    
  2. 使用 windowSplashScreenAnimatedIcon 取代起始視窗中央的圖示。如果物件透過 AnimationDrawableAnimatedVectorDrawable 建立動畫屬性及可繪項目,則也必須設定 windowSplashScreenAnimationDuration 以在顯示起始視窗的同時播放動畫。

    <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item>
    
  3. 使用 windowSplashScreenAnimationDuration 表示啟動畫面圖示動畫的持續時間。做此設定不會影響啟動畫面的實際顯示時間,不過您可以在使用 SplashScreenView#getIconAnimationDuration 自訂啟動畫面結束動畫時進行擷取。請參閱下一節的延長啟動畫面的停留時間部分,瞭解更多資訊。

    <item name="android:windowSplashScreenAnimationDuration">1000</item>
    
  4. 使用 windowSplashScreenIconBackgroundColor 來設定啟動畫面圖示後的背景。如果視窗背景與圖示之間的對比度不夠高,這項功能就能派上用場。

    <item name="android:windowSplashScreenIconBackgroundColor">@color/...</item>
    
  5. 或者,您也可以使用 windowSplashScreenBrandingImage 來設定要顯示在啟動畫面底部的圖片。設計指南不建議使用品牌宣傳圖片。

    <item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
    

延長啟動畫面在螢幕上的停留時間

應用程式會在首次繪製頁框時立即關閉啟動畫面。如果您需要非同步載入少量資料(例如本機磁碟的應用程式內設定),可以使用 ViewTreeObserver.OnPreDrawListener 暫停應用程式繪製其首個頁框。

如果您在繪圖前就啟用活動完成(例如沒有在 onResume 前設定內容檢視畫面並完成設定),就不需要預先繪製事件監聽器。

Kotlin

// Create a new event for the activity.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Set the layout for the content view.
    setContentView(R.layout.main_activity)

    // Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Check if the initial data is ready.
                return if (viewModel.isReady) {
                    // The content is ready; start drawing.
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                } else {
                    // The content is not ready; suspend.
                    false
                }
            }
        }
    )
}

Java

// Create a new event for the activity.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Set the layout for the content view.
    setContentView(R.layout.main_activity);

    // Set up an OnPreDrawListener to the root view.
    final View content = findViewById(android.R.id.content);
    content.getViewTreeObserver().addOnPreDrawListener(
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // Check if the initial data is ready.
                    if (mViewModel.isReady()) {
                        // The content is ready; start drawing.
                        content.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    } else {
                        // The content is not ready; suspend.
                        return false;
                    }
                }
            });
}

自訂用於關閉啟動畫面的動畫

您可以透過 Activity.getSplashScreen() 進一步自訂啟動畫面的動畫。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    splashScreen.setOnExitAnimationListener { splashScreenView ->
        // Create your custom animation.
        val slideUp = ObjectAnimator.ofFloat(
            splashScreenView,
            View.TRANSLATION_Y,
            0f,
            -splashScreenView.height.toFloat()
        )
        slideUp.interpolator = AnticipateInterpolator()
        slideUp.duration = 200L

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.doOnEnd { splashScreenView.remove() }

        // Run your animation.
        slideUp.start()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...

    // Add a callback that's called when the splash screen is animating to
    // the app content.
    getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);

        // Call SplashScreenView.remove at the end of your custom animation.
        slideUp.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                splashScreenView.remove();
            }
        });

        // Run your animation.
        slideUp.start();
    });
}

透過啟動此回呼,會在啟動畫面上開始動畫向量可繪項目。視應用程式啟動時間長度而定,可繪項目可能會出現在動畫的中間。使用 SplashScreenView.getIconAnimationStart 瞭解動畫開始的時間。可按照以下方法計算圖示動畫的剩餘時間:

Kotlin

// Get the duration of the animated vector drawable.
val animationDuration = splashScreenView.iconAnimationDuration
// Get the start time of the animation.
val animationStart = splashScreenView.iconAnimationStart
// Calculate the remaining duration of the animation.
val remainingDuration = if (animationDuration != null && animationStart != null) {
    (animationDuration - Duration.between(animationStart, Instant.now()))
        .toMillis()
        .coerceAtLeast(0L)
} else {
    0L
}

Java

// Get the duration of the animated vector drawable.
Duration animationDuration = splashScreenView.getIconAnimationDuration();
// Get the start time of the animation.
Instant animationStart = splashScreenView.getIconAnimationStart();
// Calculate the remaining duration of the animation.
long remainingDuration;
if (animationDuration != null && animationStart != null) {
    remainingDuration = animationDuration.minus(
            Duration.between(animationStart, Instant.now())
    ).toMillis();
    remainingDuration = Math.max(remainingDuration, 0L);
} else {
    remainingDuration = 0L;
}