Android 15以降のステータスバー領域保持
Android 15で導入されたenableEdgeToEdge()
は画面を端まで使えるようにしますが、ステータスバーのスペースが無視され、アプリのコンテンツがステータスバーと重なる問題が発生します。ここではenableEdgeToEdge()
使用中にステータスバーの高さを保持し、透過バーの下にコンテンツを表示する最適な手法を解説します。
🔍 問題の本質
enableEdgeToEdge()
適用後、システムは以下の変化をもたらします:
- ステータスバーの透過化が自動で有効になる
- コンテンツがステータスバー領域に重なり表示される
- 従来の
fitsSystemWindows="true"
やwindowTranslucentStatus
が効果を失う
👉 結果として通知アイコンとアプリのヘッダーが重なり、UIの操作性が低下します。
✅ 推奨解決策: WindowInsetsで動的にパディング調整
Android公式に準拠した方法です。全バージョン対応が可能で、システムバーの変化にも柔軟に対応できます。
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() // エッジツーエージェ有効化
setContentView(R.layout.activity_main)
// ルートビューの取得
val root: ViewGroup = findViewById(android.R.id.content)
ViewCompat.setOnApplyWindowInsetsListener(root) { view, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
// 全子ビューにステータスバーの高さを反映
view.children.forEach { child ->
child.updatePadding(top = systemBars.top)
}
WindowInsetsCompat.CONSUMED // インセット消費を宣言
}
}
特徴と長所
- 👏 動的対応: 画面回転やシステムバーの表示変化を自動検知
- ⚡ 軽量処理:
WindowInsetsCompat.CONSUMED
で重複実行防止 - 🗂 汎用性: Activity/Fragment/Compose全てで適用可
- 📱 バージョン互換: Android 5.0 (API 21) 以降をサポート
実装ポイント
- ルートビュー(
android.R.id.content
)でリスナー設定 systemBars.top
でステータスバーの高さ取得updatePadding()
でコンテンツ領域を調整- 必須:
WindowInsetsCompat.CONSUMED
でイベント消費を通知
注意
特定の子ビューのみ調整したい場合:
ViewCompat.setOnApplyWindowInsetsListener(binding.header) { view, insets ->
val statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
view.updatePadding(top = statusBarHeight)
WindowInsetsCompat.CONSUMED
}
⚠ 非推奨な回避策(一時的対応のみ)
windowOptOutEdgeToEdgeEnforcement
を使うと強制的に無効化できますが、将来の互換性は保証されません。
<style name="AppTheme" parent="Theme.Material3.DayNight">
<item name="windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
❗ この手法の問題点
- Android 15以降限定の属性(旧OSでクラッシュの危険)
- 公式が「一時的措置(temporary opt-out)」と明記
- システムのUI改善機能を完全無効化
重要
次の方法は Android 14以前のみ有効 で、Android 15以降では機能しません:
<!-- 動作しない非推奨手法 -->
<item name="android:windowTranslucentStatus">false</item>
<item name="android:fitsSystemWindows">true</item>
設計時のベストプラクティス
- 常に Jetpack Core の WindowInsetsCompat を使用
- ステータスバーの背景色設定xml
<item name="android:statusBarColor">@android:color/transparent</item>
- ライト/ダークモード対応kt
WindowCompat.getInsetsController(window, window.decorView).apply { isAppearanceLightStatusBars = !isDarkMode }
よくある質問
Q: フラグメント単位で制御できますか?
→ Fragment#getView()
でルートビュー取得後、同様にリスナーを設定可能
Q: ナビゲーションバーのスペースも保持するには?
→ systemBars.bottom
を取得し、同様にパディングを設定
view.updatePadding(bottom = systemBars.bottom)
Q: Composeでの実装方法は?
→ WindowInsets
APIとModifier.padding()
を併用:
WindowInsets.systemBars.only(WindowInsetsSides.Top)
.toPaddingValues()
Android 15以降では、動的なパディング調整が最も信頼性の高いソリューションです。WindowInsetsCompat
を活用することで、OSバージョン差異を吸収しながら最適なユーザー体験を提供できます。