AndroidアプリのEdge-to-edge対応メモ
こんにちは、トリです。
10月16日にAndroid15の配信が開始されました。
Android15から「Edge-to-edge」がデフォルトで適用されます。
対応のために試行錯誤したので、備忘録として簡単に調査内容をまとめておこうと思います。
View ベースの場合
公式曰く、Material Components (com.google.android.material)を使用している場合、一部を除き追加作業は不要とのことです。
しかし、Material Componentsを使用せずレイアウトしている場合、あるいはBottomSheetなどは、ViewCompat.setOnApplyWindowInsetsListener を使用してPaddingを設定する必要があります。
Material Components使用のレイアウトでやること
「AppBarLayout」があれば「android:fitsSystemWindows="true"」を追加します。
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp" />
</com.google.android.material.appbar.AppBarLayout>
上記に該当しないレイアウトでやること
「ViewCompat.setOnApplyWindowInsetsListener」でWindowInsetsを取得し、ステータスバーやナビゲーションバーなどのサイズを取得してPaddingを設定します。
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
val bars = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout(),
)
v.updatePadding(
left = bars.left,
top = bars.top,
right = bars.right,
bottom = bars.bottom,
)
WindowInsetsCompat.CONSUMED
}
ナビゲーションバーについて
ナビゲーションバーは2種類あるので、それぞれテストしておくと良さそうです。
3ボタンナビゲーション
ジェスチャーハンドルのナビゲーション
BottomSheetDialogFragmentでの悩み
「ViewCompat.setOnApplyWindowInsetsListener」でPaddingを設定しましたが、スクロール可能な状態だとステータスバーに重なってしまいました。
すぐ解決できそうになかったので、一旦下記のコードでステータスバーに重ならない、かつナビゲーションバーはEdge-to-edgeになるよう対処しました。
themes.xml
<style name="AppTheme" parent="@style/Theme.MaterialComponents.Light.DarkActionBar">
...
<item name="bottomSheetDialogTheme">@style/MaterialBottomSheetDialogTheme</item>
</style>
<style name="MaterialBottomSheetDialogTheme" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
...
<item name="enableEdgeToEdge">false</item>
</style>
MyBottomSheetDialogFragment.kt
class MyBottomSheetDialogFragment : BottomSheetDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val bottomSheetDialog = object : BottomSheetDialog(requireContext(), theme) {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
window?.let {
WindowCompat.setDecorFitsSystemWindows(it, false)
}
findViewById<View>(com.google.android.material.R.id.container)?.apply {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
insets.apply {
val bars = getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(top = bars.top)
}
}
}
}
}
return bottomSheetDialog
}
// ...
}
Composeベースの場合
Material3のScaffoldやTopAppBarは、デフォルトでWindowInsetsが設定されています。Material2は、必要に応じて手動で設定する必要がありそうです。
Material3のScaffold使用のコンポーザブルでやること
ScaffoldのcontentWindowInsetsはデフォルトの場合、「WindowInsetsCompat.Type.systemBars()」が設定されています。
systemBarsは「statusBars」「navigationBars」「captionBar」を含みます。
端末を縦向きのみで使用するのであれば、デフォルトのままで問題ないと思います。
Scaffold(
modifier = Modifier,
) { paddingValues ->
Box(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize(),
) {
LazyColumn {
...
}
}
}
たとえば、端末の横向き対応(DisplayCutoutへの対処)、キーボード入力を想定し、「safeCustomDrawing」という拡張関数を作成して、contentWindowInsetsに設定できます。
Scaffold(
modifier = Modifier,
contentWindowInsets = WindowInsets.safeCustomDrawing,
) { paddingValues ->
Box(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize(),
) {
LazyColumn {
...
}
}
}
val WindowInsets.Companion.safeCustomDrawing: WindowInsets
@Composable
get() = WindowInsets.statusBars
.union(WindowInsets.captionBar)
.union(WindowInsets.ime)
.union(WindowInsets.displayCutout)
navigationBars分のBottomPaddingは、LazyColumnの末尾に設定することで、ナビゲーションバーエリアまでコンテンツを広く描画できます。
LazyColumn {
items(list) { ... }
item {
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
}
}
ModifierでWindowInsetsを設定したい
「Modifier.statusBarsPadding()」や「Modifier.navigationBarsPadding()」などWindowInsetsタイプごとに用意されているので、必要に応じて利用できます。
参考
この記事が気に入ったらサポートをしてみませんか?