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を実現できます。