Skip to content

Androidエッジツーエッジ実装とステータスバーの処理(SDK 35+)

問題の説明

Android SDK 35(Android 15)にアップデートすると、アプリのUIがステータスバー領域から直接描画され始める現象が発生します。従来はステータスバー直下から始まっていたUIが、エッジツーエッジ表示へと変更された結果です。この変更により:

  • android:fitsSystemWindows="true"設定時にはステータスバーとAppBarLayoutの色が一致
  • プログラム的にインセット処理すると色が不一致になる
  • window.setStatusBarColor()がAndroid 15以降で無効化
  • ステータスバーアイコン色の制御も期待通り動作しない

この挙動の変化は、Androidのユーザーインターフェース設計における重要な変更を反映しています。

エッジツーエッジ実装の基本原則

エッジツーエッジ表示はAndroid 15以降で標準動作となりました。主な目的は:

  • 画面領域を最大化
  • 没入型体験の提供
  • ステータスバーやナビゲーションバーと連動したデザイン実現

適切なエッジツーエッジ実装手法

ステップ1:EdgeToEdgeの有効化(基本設定)

kotlin
// 既存のActivityクラス内で有効化
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge() // AndroidX Activity KTX提供
    setContentView(R.layout.activity_main)
}

ステップ2:AppBarLayoutのインセット処理(Kotlin例)

kotlin
ViewCompat.setOnApplyWindowInsetsListener(
    findViewById(R.id.appbar)
) { v, insets ->
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    v.updatePadding(top = systemBars.top)
    insets
}

ステップ3:ステータスバーアイコンの色制御

kotlin
WindowCompat.getInsetsController(window, window.decorView).apply {
    isAppearanceLightStatusBars = !isDarkModeEnabled // ダークモードに応じて調整
}

注意点

window.setStatusBarColor()はAndroid 15以降で完全に非推奨になったわけではありませんが、動作が変更されました。新しいAPIではステータスバーの背景が自動的に透明に設定されるため、明示的な色設定は効果がない場合があります。

後方互換性の実装(API 26+対応)

Android 14以前もサポートする場合の拡張実装:

gradle
dependencies {
    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.activity:activity-ktx:1.8.0")
}
kotlin
// 互換性保持用拡張関数
fun Activity.applyEdgeToEdge() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        enableEdgeToEdge()
    } else {
        // Android 10以下用代替処理
        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    }
    
    findViewById<View>(R.id.main).let { rootView ->
        ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
            insets.getInsets(WindowInsetsCompat.Type.systemBars()).let { systemBars ->
                v.updatePadding(
                    left = systemBars.left,
                    top = systemBars.top,
                    right = systemBars.right,
                    bottom = systemBars.bottom
                )
            }
            insets
        }
    }
}

よくある問題の解決法

1. ステータスバーとAppBarLayoutの色不一致

解決策

AppBarLayoutに直接パディングを設定するのではなく、コンテンツ領域に背景色を適用:

xml
<com.google.android.material.appbar.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary">

    <!-- ステータスバー分の余白を確保 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>

2. ナビゲーションバーとの干渉対策

kotlin
ViewCompat.setOnApplyWindowInsetsListener(fab) { view, insets ->
    insets.getInsets(WindowInsetsCompat.Type.navigationBars()).let { navBars ->
        (view.layoutParams as CoordinatorLayout.LayoutParams).apply {
            bottomMargin = navBars.bottom + 16.dp // デフォルトマージンに追加
        }
    }
    insets
}

3. Viewの初期化タイミング問題

View.getRootWindowInsets()がnullを返す場合の対策実装:

kotlin
// Viewアタッチリスナー登録による遅延初期化
view.doOnAttach { v ->
    WindowInsetsControllerCompat(window, v).let { controller ->
        controller.isAppearanceLightStatusBars = true
    }
    
    ViewCompat.requestApplyInsets(v) // インセット再要求
}

エッジツーエッジによる影響と対策

影響内容対策方法
高さ計算の誤差ViewCompat.setOnApplyWindowInsetsListenerでコンテンツ高更新
ナビゲーション障害インタラクション領域に最小タップサイズ(48dp)確保
入力フィールド隠蔽WindowInsetsCompat.Type.ime()でキーボード挙動調整
カメラノッチ領域衝突layoutInDisplayCutoutModeプロパティで設定

結論

Android 15以降のエッジツーエッジ実装で重要なポイント:

  • fitsSystemWindows="true"より動的インセット処理が優位
  • AppBarLayoutに状態バー領域分パディング適用が必須
  • WindowInsetsControllerCompatを使用したステータスバー制御が安定
  • 後方互換実装にはViewCompatクラスが不可欠

正しい実装によって画面全体を有効活用し、Androidデザインガイドラインに準拠したモダンなUIを実現できます。