LoginSignup
68
52

More than 1 year has passed since last update.

Android 12 のスプラッシュ画面 (Splash Screen) に対応する

Last updated at Posted at 2021-08-30

この記事で解説している AndroidX Core SplashScreen のバージョンは 1.0.0 です。


Android 12 からアプリ起動時にスプラッシュ画面 (Splash Screen) が表示されるようになりました。

また、アプリ独自のスプラッシュ画面を実装している場合に、targetSdk = 31 (Android 12) 以上にすると Lint がスプラッシュ画面と思われる Activity について、以下の Warning を表示するようになります。

The application should not provide its own launch screen

splashactivity_lint.png

この記事では既存のアプリを Android 12 以上のスプラッシュ画面に対応する場合の具体的な手段についてまとめています。

公式ドキュメントにはスプラッシュ画面の説明とマイグレーションガイドが存在します。この記事ではその内容をまとめています。

スプラッシュ画面が表示される条件

Android アプリを起動するときには 3 つの状態があります。

状態名 状態 スプラッシュ画面
コールドスタート アプリが完全に終了している状態からの起動 スプラッシュ画面あり
ウォームスタート システムによりプロセスや Activity が破棄された状態からの起動 スプラッシュ画面あり
ホットスタート Activity がメモリ上に生存している状態からの起動 スプラッシュ画面なし

ウォームスタートでスプラッシュ画面が表示される点に注意してください。 Android 12 未満のアプリ実装では SplashActivity などを用意してスプラッシュ画面を実現していましたが、その場合はウォームスタートで直近の Activity を表示する場合には SplashActivity が表示されることはありませんでした。 Android 12 以上ではアプリ切り替えなどでもスプラッシュ画面が表示されることがあります。

また、 Android 12 以上で新しいスプラッシュ画面を無効とする設定はないため、コールドスタートとウォームスタートでは必ずスプラッシュ画面が表示されます。

コールドスタート・ウォームスタート・ホットスタートについての詳細は以下のドキュメントを参照してください。

スプラッシュ画面のレイアウトとアニメーション

スプラッシュ画面は Android 12 以上の SplashScreenView で定められた要素で画面を構成しています。

下図における 1 はアプリアイコンです。これには AnimatedVectorDrawable XML を設定することができるため、任意のアニメーションをさせることができます。

2 はアイコンの背後に表示する円形の背景色、 4 はウィンドウ背景色です。(図の 3 はアプリアイコンの外側 1/3 がマスクされることを示しています)。

image.png

スプラッシュ画面では Adaptive icon と同様にアイコンの外側の 1/3 がマスクされてアイコンが表示されます。スプラッシュ画面用のアイコンも Adaptive icon の仕様 に従い、 108 dp x 108 dp のサイズで中央の 72dp x 72dp が表示されるようにデザインします。もちろん Vector 画像として作成するのであれば具体的なサイズはどのようにとっても構いません。

AndroidX Core SplashScreen Design

実際に表示されるレイアウトは以下のとおりです。

環境 表示レイアウト
android:windowSplashScreenIconBackgroundColor 指定なし、または Theme.SplashScreen テーマ適用 アイコンが 288dp x 288dp の領域に拡大されて配置され、中央 192dp x 192dp の円形の領域が表示されるようにマスクされます。
android:windowSplashScreenIconBackgroundColor 指定あり、または Theme.SplashScreen.IconBackground テーマ適用 アイコンが 240dp x 240dp の領域に拡大されて配置され、中央 160dp x 160dp の円形の領域が表示されるようにマスクされます。円の内側の背景はアイコン背景色で塗りつぶされます。

スプラッシュ画面が表示されるときの開始アニメーションは 1 の Drawable アニメーションだけをカスタマイズできます。

スプラッシュ画面が消えるときの終了アニメーションは Kotlin から SplashScreenView を参照して、任意の Animator を実行することが可能です。

Splash Screen Dimensions

AndroidX Core SplashScreen

スプラッシュ画面とそのアニメーションの仕組みは Android 12 から導入されましたが、AndroidX Core SplashScreen を用いることで Android 12 未満の端末でも Android 12 以上と同じタイミング・同じアニメーションでスプラッシュ画面を表示することができます。

Android 12 未満をサポートするアプリでは AndroidX Core SplashScreen を用いてスプラッシュ画面を実装します。

AndroidX Core SplashScreen は minSdk = 21 (Android 5) に対応しています。

1.0.0-beta02 の時点では、Android のバージョンによって、スプラッシュ画面の表現が異なります。

OS バージョン 見た目 スクリーンショット
Android 5 背景色のみ (Android 5)
Android 6 ~ Android 11 + Theme.SplashScreen 背景色 + アイコン + アイコンマスク

288dp x 288dp に拡大表示され、中央 192dp x 192dp が表示
(Android 11)
Android 6 ~ Android 11 + Theme.SplashScreen.IconBackground 背景色 + アイコン + アイコン背景 + アイコンマスク

240dp x 240dp に拡大表示され、中央 160dp x 160dp が表示
(Android 11)

アイコン背景を透過して、Theme からアイコン背景色を指定しています。
Android 12 ~ + Theme.SplashScreen 背景色 + アイコン + アイコンマスク

288dp x 288dp に拡大表示され、中央 192dp x 192dp が表示
(Android 12)
Android 12 ~ + Theme.SplashScreen.IconBackground 背景色 + アイコン + アイコン背景 + アイコンマスク

240dp x 240dp に拡大表示され、中央 160dp x 160dp が表示
(Android 12)

アイコン背景を透過して、Theme からアイコン背景色を指定しています。

AndroidX Core SplashScreen のデザイン指定は以下のドキュメントに記載があります。

上記の表に記載したとおり、アイコン背景なしなら 288dp x 288dp、アイコン背景ありなら 240dp x 240dp となります。

image.png

アイコンサイズと Adaptive Icon について

AndroidX Core SplashScreen では Android 11 以下で windowSplashScreenAnimatedIcon に Adaptive Icon を指定すると、以下のように本来の Adaptive Icon の表示よりも大きく拡大されて表示されてしまいます。

Android 8 ~ Android 11 + Adaptive Icon Android 12 + Adaptive Icon

これは、AndroidX Core SplashScreen が Adaptive Icon を 108dp x 108dp ではなく中央を切り出した 72dp x 72dp として認識してしまい、これを改めて 288dp x 288dp に拡大して表示してしまうためです。

windowSplashScreenAnimatedIcon には Adaptive Icon ではない Drawable を設定することをおすすめします。

既存の Adaptive Icon を流用する場合は、以下のような Drawable を用意することで Adaptive Icon ではない Drawable として利用できます。

ic_splash.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_launcher_background"/>
    <item android:drawable="@drawable/ic_launcher_foreground"/>
</layer-list>

AndroidX Core SplashScreen の設定

compileSdkVersion を 31 以上に設定します。

build.gradle.kts

//...
android {
    compileSdkVersion(31)
    //...

Gradle から core-splashscreen 依存を追加します。

build.gradle.kts

//...
dependencies {
    implementation("androidx.core:core-splashscreen:1.0.0-beta02")
    //...

Theme.SplashScreen.IconBackground または Theme.SplashScreen を継承した SplashTheme を設定します。Theme.SplashScreen.IconBackground を選択すると、アイコン背景色ありのレイアウトとなります。

themes.xml

...
    <style name="SplashTheme" parent="Theme.SplashScreen.IconBackground">
        <item name="windowSplashScreenBackground">@color/splash_background</item>
        <item name="windowSplashScreenIconBackgroundColor">@color/splash_icon_background</item>
        <item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
        <item name="windowSplashScreenAnimationDuration">0</item>
    </style>

起動 Activity に、上記 Theme を設定します。

AndroidManixest.xml

...
    <application ...>
        <activity
            android:name=".MainActivity"
           ...
            android:theme="@style/SplashTheme">
        ...

起動 Activity で installSplashScreen() を実行します。

class MainActivity: AppCompatActivity(R.layout.main_activity) {
    override fun onCreate(savedInstanceState: Bundle?) {
        // installSplashScreen() は super.onCreate() よりも前で実行する
        // スプラッシュ画面終了アニメーションを設定するときは
        // splashScreen インスタンスを保持して使用する
        // val splashScreen = installSplashScreen()
        installSplashScreen()
        // MainActivity 本来の Theme へ差し替える
        setTheme(R.style.AppTheme)
        super.onCreate(savedInstanceState)
        // ...
        // splashScreen.setOnExitAnimationListener { splashScreenViewProvider -> ... }
    }
    // ...
}

Activity.installSplashScreen() は SplashScreen インスタンスを生成して返します。postSplashScreenTheme を指定している場合はこのタイミングで Activity.setTheme() によりテーマが差し替えられますが、この動作は分かりにくいため、postSplashScreenTheme を指定せずに setTheme() を明示的に実行することをおすすめします。

installSplashScreen() という名前なので紛らわしいですが、 installSplashScreen() を実行しなくても、Theme.SplashScreen Theme に差し替えるだけで Android 12 以上のような互換スプラッシュ画面は表示されています(Theme 差し替えの仕組みは次の項目を参照)。 installSplashScreen() は互換 SplashScreen インスタンスを返すための仕組みです。

AndroidX Core SplashScreen の仕組み

AndroidX Core SplashScreen は Android 12 以上では OS の仕組みを使ってスプラッシュ画面を表示しますが、 Android 12 未満の環境では Theme の差し替えにより Android 12 以上のスプラッシュ画面と似たような挙動をするように調整されています。

Theme.SplashScreen.IconBackground Theme を指定すると、API 31 (Android 12) 未満では android:windowBackground に @drawable/compat_splash_screen を設定します。

image.png

API 21 (Android 5) に適用されるレイアウトは drawable/compat_splash_screen.xml となっており、背景単色塗りでアイコンがありません。

image.png

API 23 (Android 6.0) ~ API 30 (Android 11) に適用されるレイアウトは drawable-v23/compat_splash_screen.xml となっており、背景色、アイコン背景色、アイコンのマスク表示があります。

image.png

どちらのレイアウトにも、ブランディングロゴイメージはレイアウトされていません。ブランディングロゴイメージは Android 12 以上でしか表示できないようです。

AndroidX Core SplashScreen のテーマ属性

AndroidX Core SplashScreen を導入した場合のテーマに指定できる属性をまとめます。ライブラリ向けの属性では android: プレフィクスはつけません。

属性名 内容
windowSplashScreenBackground スプラッシュ画面の背景色
windowSplashScreenIconBackgroundColor スプラッシュ画面におけるアイコンの背景色。 Android 12 以上におけるデフォルト値は @android:color/transparent となり、transparent が指定されている場合はアイコン背景色がないものとして扱われます。また、この項目は Theme.SplashScreen.IconBackground テーマでのみ有効となるため、 Theme.SplashScreen テーマと併用すると強制的に無効扱いされます。
windowSplashScreenAnimatedIcon スプラッシュ画面中央のアニメーションアイコン。

Android 12 以上では Adaptive Icon を指定しても綺麗に表示されますが、**Android 11 以下では Adaptive Icon を指定すると通常よりも大きく拡大されて表示されてしまいます。**Android 11 以下に対応するときはこの項目には Adaptive Icon は使用しないことをおすすめします。
windowSplashScreenAnimationDuration スプラッシュ画面が表示されてから閉じるまでの時間のミリ秒 (最大1000ミリ秒)
(core-splashscreen:1.0.0-beta02 現時点では Android 12 以上でのみ有効な設定のようです)
postSplashScreenTheme 起動 Activity が本来設定すべき Theme を指定。この項目が設定されている場合は installSplashScreen() のタイミングで Activity の Theme が差し替えられます。
splashScreenIconSize アイコンサイズを指定できるようです... が、core-splashscreen:1.0.0-beta02 時点の実装を見るかぎり、この値を外部から指定することは想定されていないように見えます。core-splashscreen:1.0.0-beta02 の時点ではこの項目の設定はお勧めしません。

Android 12 以上で利用できる android:windowSplashScreenBrandingImage は AndroidX Core SplashScreen からは利用できないようです。

既存アプリのマイグレーション

既存アプリのスプラッシュ画面の仕組みにより対応は異なります。

既存のアプリで android:windowBackground によりスプラッシュ画面を実現している

Android 公式では android:windowBackground を設定することによりアプリ起動中の画面をカスタマイズすることが推奨されていました。

Android 12 以上では android:windowBackground で指定した Drawable は表示されずに、代わりにシステムによる新しいスプラッシュ画面が表示されるようになります。

この場合は android:windowBackground によるスプラッシュ画面の指定を削除し、新しいスプラッシュ画面のデザインや仕組みに沿ってデザインテーマを設定します。

AndroidX Core SplashScreen を導入し、 Theme.SplashScreen テーマを指定、 windowSplashScreenBackground に背景色、 windowSplashScreenAnimatedIcon にアイコンを設定します。アイコンはアニメーションのあるアイコンでも、アニメーションのないアイコンでも構いません。アイコン背景色を持たせたい場合は Theme.SplashScreen.IconBackground テーマを指定し、windowSplashScreenIconBackgroundColor にアイコン背景色を設定します。

既存のアプリで SplashActivity によりスプラッシュ画面を実現している

Android 公式では非推奨ではありましたが、現実には SplashActivity を実装してスプラッシュ画面を表示する設計は広く使われていました。

ここでは SplashActivity のあとに MainActivity を起動する設計となっていることを前提とします。

既存の SplashActivity 実装は様々なアプリ起動処理を集約していると思われますが、たとえば以下のような機能があります。

  • ライブラリの初期化
  • ローカルデータの読み込み
  • ローカルデータベースのマイグレーション
  • ログイン状態の読み込みとその判定
  • アプリのバージョンアップ要否判定のための API アクセスとその判定
  • Intent や DeepLink からの起動に対応して遷移先 Activity の判定と起動

Android 12 以上では SplashActivity の前にスプラッシュ画面が表示されるため、このままではスプラッシュ画面が 2 回表示されてしまいます。

既存の SplashActivity が持つロジックにも配慮しつつ新しいスプラッシュ画面に置き換えるために、以下のいずれかの方法で対応します。

  • 対応1: SplashActivity をそのまま残すが、SplashActivity を描画させないようにすることでスプラッシュ画面が2重に表示されないようにする
  • 対応2: 新しいスプラッシュ画面も SplashActivity もどちらも表示させるが、新しいスプラッシュ画面の終了アニメーションをカスタマイズすることで違和感のないように SplashActivity が表示されるようにする
  • 対応3: SplashActivity を削除し、SplashActivity に実装されていたロジックを MainActivity などに移して対応する

対応1: SplashActivity を残すが、SplashActivity を描画させない

この構成は以下のとおりです。

スプラッシュ画面 → SplashActivity (描画しない) → MainActivity

既存のアプリのロジックに影響を与えずに新しいスプラッシュ画面だけの表示に対応するもっとも簡単な方法です。

この対応は既存の SplashActivity のロジックをそのまま使えるという点でマイグレーションが簡単というメリットがあります。

表示されることのない SplashActivity を生成する時間が無駄にかかってしまう点がデメリットになります。なるべく SplashActivity を取り除くような対応も検討してください。

  1. AndroidX Core SplashScreen を導入する
  2. SplashActivity に AndroidX Core SplashScreen 向けに設定した Theme を設定する
  3. SplashActivity を以下のように書き換えて、SplashActivity が描画されないようにする
class SplashActivity: AppCompatActivity(R.layout.splash_activity) {
    override fun onCreate(savedInstanceState: Bundle?) {
        val splashScreen = installSplashScreen()
        setTheme(R.style.AppTheme)
        super.onCreate(savedInstanceState)
        splashScreen.setKeepOnScreenCondition {
            // スプラッシュ画面を表示し続ける (SplashActivity の描画を停止している)
            // true を返すと、内部的に OnPreDrawListener.onPreDraw による描画停止が実行されています
            true
        }
        // ...
    }
    // ...
}

対応2: スプラッシュ画面も SplashActivity も表示する

この構成は以下のとおりです。

スプラッシュ画面 → スプラッシュ画面終了アニメーション → SplashActivity → MainActivity

アプリのブランディングの観点から、既存の SplashActivity で実現していたデザインやアニメーションを Android 12 以上でも表示させたいという要望があり、スプラッシュ画面のアニメーションでは表現しきれない場合の対応です。

Android 12 以上でのスプラッシュ画面は必ず表示されます。新しいスプラッシュ画面の終了アニメーションを工夫して SplashActivity が表示されても違和感のないようにし、ユーザーからは一つのスプラッシュ画面が表示されたように感じさせます。

この対応は Android 12 以上の SplashScreenView (AndroidX Core SplashScreen では SplashScreenViewProvider.view) だけでは表現できないスプラッシュ画面の表現ができ、既存の SplashActivity のロジックもそのまま残すことができるというメリットがあります。

SplashScreenView に存在しない要素をスムーズに SplashActivity に繋げることはできないので、違和感なく SplashActivity が表示されるにはどのようにデザインしたら良いか悩ましい点はデメリットです。また、ウォームスタートでもスプラッシュ画面が表示されてしまうことを考えると、スプラッシュ画面のあとに SplashActivity が表示されなくても違和感のないような終了アニメーションを考える必要があります。

この方法は Android 12 以上のスプラッシュ画面の設計とはあまり相性が良いとは言えず、おすすめできません。

スプラッシュ画面の終了アニメーションは以下のドキュメントを参照してください。 SplashScreenView と IconView に対してアニメーションを適用できるようです。

対応3: SplashActivity を削除する

この構成は以下のとおりです。

スプラッシュ画面 → MainActivity

SplashActivity に実装していたロジックを MainActivity などに移植することで、 SplashActivity を完全に削除します。

この方法はもっともアプリ起動の UX が高く、Android 公式として推奨されている対応です。

SplashActivity に実装していたロジックを Application.onCreate() や MainActivity.onCreate() などの適切な場所に移しつつ、起動時に必要な処理をなるべく少なくしたり、placeholder となるスケルトン UI を表示するなどの工夫により、アプリ起動を高速化します。

以下に、時系列順にアプリ起動の適切な実装について説明します。この時系列順の説明は Remove the custom splash screen Activity に記載されているテクニックを駆使するとどのような設計となるかをまとめたものです。

  • アプリ起動開始
  • スプラッシュ画面が表示され、スプラッシュ画面のアニメーションが開始される
  • Applicaiton.onCreate()
    • 最低限必要なライブラリを読み込み、初期化する
      • AndroidX App Startup を活用し、なるべく Application.onCreate() での初期化処理を減らす
    • ローカルデータベースのマイグレーションを実行する
      • 時間のかかる重い処理ですが、アプリのバージョンアップ直後の起動のときのみ実行される処理であるため Application.onCreate() で実装するという判断も許容されます
      • ただし、 App startup time に示されている 5 秒を超えるようなマイグレーションは Applictaion.onCreate() では対応せず、専用のマイグレーション画面を表示して対応する必要があります
  • MainActivity.onCreate()
    • super.onCreate(savedInstanceState)
    • installSplashScreen()
    • setTheme(R.style.AppTheme)
    • setContentView(R.layout.main_activity)
    • OnPreDrawListener.onPreDraw / splashScreen.setKeepOnScreenCondition により描画を停止する
  • ログイン状態など、軽量なローカルデータを読み込む
  • Intent や DeepLink の遷移や、ログイン画面の表示など、画面遷移が必要であればここで画面遷移を実行する
  • OnPreDrawListener.onPreDraw / splashScreen.setKeepOnScreenCondition により描画を再開する
  • スプラッシュ画面の終了アニメーションが開始され、スプラッシュ画面が終了する
  • MainActivity が表示される
  • キャッシュからアプリトップコンテンツを表示する
    • キャッシュがなければスケルトン UI を表示する
  • バージョンアップ判定 API などの確認が必要であればアクセスする
    • バージョンアップ誘導が必要であれば導線を表示したり画面遷移したりする
  • アプリトップコンテンツを読み込むための API へアクセスする
    • API レスポンスを受け取ったらアプリトップコンテンツの表示とキャッシュを更新する
  • すべての UI とコンテンツが表示され、ユーザーが操作可能になる

慎重に起動処理を組み立てる必要がありますが、このように対応することでスプラッシュ画面表示中の処理を最低限に保ち、ユーザーがアプリを起動してから操作できるようになるまでの待機時間がもっとも短くなります。

68
52
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
52