Android

ComponentActivityのenableEdgeToEdgeの動作を確認する

Katz

Activity v1.8.0からComponentActivityにenableEdgeToEdgeというメソッドが追加されて、EdgeToEdge対応が楽にできるという情報を得たので、簡単にですがコードと動作を確認してみたいと思います。

※ちなみに今回の動作確認では以下の依存関係を利用しています。

Kotlin
dependencies {
    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
    implementation("androidx.activity:activity-compose:1.8.0")
    implementation(platform("androidx.compose:compose-bom:2023.03.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}

ComponentActivityのenableEdgeToEdgeのコードを見てみる

ComponentActivityのenableEdgeToEdgeのコードを見てみると各SDKバージョンごとに処理が分岐していることがわかる。そのため各SDKバージョンごとにEdgeToEdgeのセットアップに必要な処理が異なるということがわかる。

バージョン条件 呼び出す関数
SDKバージョンが29以上 EdgeToEdgeApi29
SDKバージョンが26〜28 EdgeToEdgeApi26
SDKバージョンが23〜25 EdgeToEdgeApi23
SDKバージョンが21〜22 EdgeToEdgeApi22
SDKバージョンが20以下 EdgeToEdgeBase
ComponentActivity.kt
@JvmName("enable")
@JvmOverloads
fun ComponentActivity.enableEdgeToEdge(
    statusBarStyle: SystemBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT),
    navigationBarStyle: SystemBarStyle = SystemBarStyle.auto(DefaultLightScrim, DefaultDarkScrim)
) {
    val view = window.decorView
    val statusBarIsDark = statusBarStyle.detectDarkMode(view.resources)
    val navigationBarIsDark = navigationBarStyle.detectDarkMode(view.resources)
    val impl = Impl ?: if (Build.VERSION.SDK_INT >= 29) {
        EdgeToEdgeApi29()
    } else if (Build.VERSION.SDK_INT >= 26) {
        EdgeToEdgeApi26()
    } else if (Build.VERSION.SDK_INT >= 23) {
        EdgeToEdgeApi23()
    } else if (Build.VERSION.SDK_INT >= 21) {
        EdgeToEdgeApi21()
    } else {
        EdgeToEdgeBase()
    }.also { Impl = it }
    impl.setUp(
        statusBarStyle, navigationBarStyle, window, view, statusBarIsDark, navigationBarIsDark
    )
}

それぞれのSDKバージョンのEdgeToEdgeのコードを見てみる

それぞれのSDKバージョンのEdgeToEdgeのコードを見てみると、各SDKバージョンごとにStatusBarやNavigationBarの仕様が異なることがわかる。つまるところenableEdgeToEdgeは各SDKバージョンごとに異なるStatusBarとNavigationBarの仕様を吸収してくれる便利な関数らしい。

EdgeToEdgeApi29

Kotlin
@RequiresApi(29)
private class EdgeToEdgeApi29 : EdgeToEdgeImpl {

    @DoNotInline
    override fun setUp(
        statusBarStyle: SystemBarStyle,
        navigationBarStyle: SystemBarStyle,
        window: Window,
        view: View,
        statusBarIsDark: Boolean,
        navigationBarIsDark: Boolean
    ) {
        // アプリを全画面で表示するようにする
        WindowCompat.setDecorFitsSystemWindows(window, false)
        // 指定したスクリムの色をStatusBarにセットする
        window.statusBarColor = statusBarStyle.getScrimWithEnforcedContrast(statusBarIsDark)
        // 指定したスクリムの色をNavigationBarにセットする
        window.navigationBarColor =
            navigationBarStyle.getScrimWithEnforcedContrast(navigationBarIsDark)
        // 完全に透明な背景が要求された場合に、システムがステータスバーに十分なコントラストを確保しているかどうか
        window.isStatusBarContrastEnforced = false
        // 完全に透明な背景が要求された場合に、システムがナビゲーションバーに十分なコントラストを確保しているかどうか
        window.isNavigationBarContrastEnforced =
            navigationBarStyle.nightMode == UiModeManager.MODE_NIGHT_AUTO
        WindowInsetsControllerCompat(window, view).run {
            // ステータス・バーのフォアグラウンドがライトに設定するか
            isAppearanceLightStatusBars = !statusBarIsDark
            // ナビゲーションバーのフォアグラウンドがライトに設定するか   
            isAppearanceLightNavigationBars = !navigationBarIsDark
        }
    }
}

EdgeToEdgeApi26

Kotlin
@RequiresApi(26)
private class EdgeToEdgeApi26 : EdgeToEdgeImpl {

    @DoNotInline
    override fun setUp(
        statusBarStyle: SystemBarStyle,
        navigationBarStyle: SystemBarStyle,
        window: Window,
        view: View,
        statusBarIsDark: Boolean,
        navigationBarIsDark: Boolean
    ) {
        // アプリを全画面で表示するようにする
        WindowCompat.setDecorFitsSystemWindows(window, false)
        // 指定したスクリムの色をStatusBarにセットする
        window.statusBarColor = statusBarStyle.getScrim(statusBarIsDark)
        // 指定したスクリムの色をNavigationBarにセットする
        window.navigationBarColor = navigationBarStyle.getScrim(navigationBarIsDark)
        WindowInsetsControllerCompat(window, view).run {
            // ステータス・バーのフォアグラウンドがライトに設定するか
            isAppearanceLightStatusBars = !statusBarIsDark
            // ナビゲーションバーのフォアグラウンドがライトに設定するか   
            isAppearanceLightNavigationBars = !navigationBarIsDark
        }
    }
}

EdgeToEdgeApi23

Kotlin
@RequiresApi(23)
private class EdgeToEdgeApi23 : EdgeToEdgeImpl {

    @DoNotInline
    override fun setUp(
        statusBarStyle: SystemBarStyle,
        navigationBarStyle: SystemBarStyle,
        window: Window,
        view: View,
        statusBarIsDark: Boolean,
        navigationBarIsDark: Boolean
    ) {
        // アプリを全画面で表示するようにする
        WindowCompat.setDecorFitsSystemWindows(window, false)
        // 指定したスクリムの色をStatusBarにセットする
        window.statusBarColor = statusBarStyle.getScrim(statusBarIsDark)
        // 指定したスクリムの色をNavigationBarにセットする
        window.navigationBarColor = navigationBarStyle.darkScrim
        // ステータス・バーのフォアグラウンドがライトに設定するか
        WindowInsetsControllerCompat(window, view).isAppearanceLightStatusBars = !statusBarIsDark
    }
}

EdgeToEdgeApi21

Kotlin
@RequiresApi(21)
private class EdgeToEdgeApi21 : EdgeToEdgeImpl {

    @Suppress("DEPRECATION")
    @DoNotInline
    override fun setUp(
        statusBarStyle: SystemBarStyle,
        navigationBarStyle: SystemBarStyle,
        window: Window,
        view: View,
        statusBarIsDark: Boolean,
        navigationBarIsDark: Boolean
    ) {
        // アプリを全画面で表示するようにする
        WindowCompat.setDecorFitsSystemWindows(window, false)
        // ステータスバーとナビゲーションバーを半透明にする
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
    }
}

EdgeToEdgeBase

Kotlin
private class EdgeToEdgeBase : EdgeToEdgeImpl {

    override fun setUp(
        statusBarStyle: SystemBarStyle,
        navigationBarStyle: SystemBarStyle,
        window: Window,
        view: View,
        statusBarIsDark: Boolean,
        navigationBarIsDark: Boolean
    ) {
        // No edge-to-edge before SDK 21.
    }
}

ComponentActivityのenableEdgeToEdgeの動作を確認する

なんとなくComponentActivityのenableEdgeToEdgeの動作がわかったので動作確認をしてみる。ちなみにstatusBarStyleとnavigationBarStyleのパラメータの型であるStatusBarStyleを変えることで、以下のように挙動を変えることができるみたいなので、これを変えながら動作確認をしてみる。

タイプ パラメータ1 パラメータ2 パラメータ3 解説
StatusBarStyle.light scrim : 背景色 darkScrim : 白いシステムアイコンしか端末が表示できない時に利用する背景色 Scrimに明るい色が指定されることが想定される時に利用される(ライトモード)
StatusBarStyle.dark scrim : 背景色 Scrimに暗い色が指定されることが想定される時に利用される(ダークモード)
StatusBarStyle.auto lightScrim : ライトモード時の背景色 darkScrim: ダークモード時の背景色 detectDarkMode : ライトモードとダークモードを検知する条件を定義 ライトモード・ダークモードの切り替えに連動して色を変更したい時に利用する
Kotlin
class SystemBarStyle private constructor(
    private val lightScrim: Int,
    internal val darkScrim: Int,
    internal val nightMode: Int,
    internal val detectDarkMode: (Resources) -> Boolean
) {

    companion object {
        @JvmStatic
        @JvmOverloads
        fun auto(
            @ColorInt lightScrim: Int,
            @ColorInt darkScrim: Int,
            detectDarkMode: (Resources) -> Boolean = { resources ->
                (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
                    Configuration.UI_MODE_NIGHT_YES
            }
        ): SystemBarStyle {
            return SystemBarStyle(
                lightScrim = lightScrim,
                darkScrim = darkScrim,
                nightMode = UiModeManager.MODE_NIGHT_AUTO,
                detectDarkMode = detectDarkMode
            )
        }

        @JvmStatic
        fun dark(@ColorInt scrim: Int): SystemBarStyle {
            return SystemBarStyle(
                lightScrim = scrim,
                darkScrim = scrim,
                nightMode = UiModeManager.MODE_NIGHT_YES,
                detectDarkMode = { _ -> true }
            )
        }

        @JvmStatic
        fun light(@ColorInt scrim: Int, @ColorInt darkScrim: Int): SystemBarStyle {
            return SystemBarStyle(
                lightScrim = scrim,
                darkScrim = darkScrim,
                nightMode = UiModeManager.MODE_NIGHT_NO,
                detectDarkMode = { _ -> false }
            )
        }
    }

    internal fun getScrim(isDark: Boolean) = if (isDark) darkScrim else lightScrim

    internal fun getScrimWithEnforcedContrast(isDark: Boolean): Int {
        return when {
            nightMode == UiModeManager.MODE_NIGHT_AUTO -> Color.TRANSPARENT
            isDark -> darkScrim
            else -> lightScrim
        }
    }
}



SystemBarStyle.light

SystemBarStyle.lightを指定したときの動作を以下のコードで確認してみる。

Kotlin
enableEdgeToEdge(
    statusBarStyle = SystemBarStyle.light(scrim = Color.RED, darkScrim = Color.BLUE),
    navigationBarStyle = SystemBarStyle.light(scrim = Color.RED, darkScrim = Color.BLUE)
)

EdgeToEdgeApi29

  • StatusBarとNavigationBarはScrimの色が反映されて赤色になる

EdgeToEdgeApi26

  • StatusBarとNavigationBarはScrimの色が反映されて赤色になる

EdgeToEdgeApi23

  • StatusBarはScrimの色が反映されて赤色になる
  • NavigationBarはDarkScrimの色が反映されて青色になる
    • システムアイコンが白色しか対応していないないのでDarkScrimが適用されるっぽい

EdgeToEdgeApi21

  • StatusBarもNavigationBarもScrimの色は反映されずに半透明になる
    • StatusBarやNavigationBartの背景色の指定ができないSDKバージョンのため

SystemBarStyle.dark 

SystemBarStyle.darkを指定したときの動作を以下のコードで確認してみる。

Kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge(
            statusBarStyle = SystemBarStyle.dark(scrim = Color.BLUE),
            navigationBarStyle = SystemBarStyle.dark(scrim = Color.BLUE)
        )

        setContent {
            Surface(modifier = Modifier.fillMaxSize()) {
                LazyColumn(modifier = Modifier.fillMaxSize()) {
                    items(100) {
                        Text(text = it.toString())
                    }
                }
            }
        }
    }
}

EdgeToEdgeApi29

  • StatusBarとNavigationBarはScrimの色が反映されて青色になる

EdgeToEdgeApi26

  • StatusBarとNavigationBarはScrimの色が反映されて青色になる

EdgeToEdgeApi23

  • StatusBarとNavigationBarはScrimの色が反映されて青色になる
    • NavigationBarのシステムアイコンは白色のみしか対応していないが、暗い背景色が指定される想定なのでScrimに指定した色をそのまま描画

EdgeToEdgeApi21

  • StatusBarもNavigationBarもScrimの色は反映されずに半透明になる
    • StatusBarやNavigationBartの背景色の指定ができないSDKバージョンのため

SystemBarStyle.auto

Kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge(
            statusBarStyle = SystemBarStyle.auto(lightScrim = Color.RED, darkScrim = Color.BLUE),
            navigationBarStyle = SystemBarStyle.auto(
                lightScrim = Color.GREEN,
                darkScrim = Color.YELLOW
            )
        )

        setContent {
            MaterialTheme(
                colorScheme = if(isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
            ) {
                Surface(modifier = Modifier.fillMaxSize()) {
                    LazyColumn(modifier = Modifier.fillMaxSize()) {
                        items(100) {
                            Text(text = it.toString())
                        }
                    }
                }
            }
        }
    }
}

EdgeToEdgeApi29

  • StatusBarとNavigationBarはScrimの色が反映されてライトモード・ダークモードかかわらず透明になる
    • StatusBarStyle.autoにするとgetScrimWithEnforcedContrastでStatusBarとNavigationBarに設定するScrimの色が透明に強制的に指定されるためこのような動作になる。

EdgeToEdgeApi26

  • StatusBarとNavigationBarはScrimの色が反映されてライトモードで赤色と青色、ダークモードで緑色と黄色になる
  • ダークモードはSDKバージョン29のはずだが、SDKバージョン28のエミュレータだと動作した

EdgeToEdgeApi23

  • StatusBarとNavigationBarはScrimの色が反映されて赤色・黄色になる
  • ダークモードはSDKバージョン29から対応のため、切り替えての動作確認はできなかった。

EdgeToEdgeApi21

  • StatusBarもNavigationBarもScrimの色は反映されずに半透明になる
  • ダークモードはSDKバージョン29から対応のため、切り替えての動作確認はできなかった。

おわりに

  • enableEdgeToEdgeを利用することでEdgeToEdge対応は比較的簡単にできるようになっている
  • enableEdgeToEdgeに指定するStatusBarStyleによってStatusBarやNavigationBarの色が変わるようになっている。StatusBarやNavigationBarの色に関してはSDKバージョンごとに動作が異なるので注意が必要

参考記事

本ページの内容を書き終えた後に見つけたのですが、以下のページにAndroid OSバージョンごとにできるEdgeToEdge対応の方法論が書かれておりました。enableEdgeToEdgeはこの方針に基づいて作成されたものっぽいのでこちらの記事を参考に見てみるとさらに理解が深まるかなと思います。

https://android-developers-jp.googleblog.com/2023/01/is-your-app-providing-a-backward-compatible-edge-to-edge-experience.html

ABOUT ME
Katz
Katz
Androidエンジニア
AndroidエンジニアをやっているKatzです。最近はKotlin Multiplatformを中心にやっています。経歴やお仕事の依頼については、私のプロフィールに詳細を記載していますので、ご確認ください。
記事URLをコピーしました