AndroidアプリのEdge-to-edge対応メモ

こんにちは、トリです。
10月16日にAndroid15の配信が開始されました。

Android15から「Edge-to-edge」がデフォルトで適用されます。

Android 15 搭載デバイスでは、デフォルトでアプリがエッジ ツー エッジ対応になります。 Android 15(API レベル 35)がターゲットとなります。

https://developer.android.com/about/versions/15/behavior-changes-15?hl=ja#window-insets

対応のために試行錯誤したので、備忘録として簡単に調査内容をまとめておこうと思います。

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
}

TIPS:ナビゲーションバーについて

ナビゲーションバーは2種類あるので、それぞれテストしておくと良さそうです。

  • 3ボタンナビゲーション

  • ジェスチャーハンドルのナビゲーション

ナビゲーションバー2種

TIPS:BottomSheetDialogFragmentでの悩み

「ViewCompat.setOnApplyWindowInsetsListener」でPaddingを設定しましたが、スクロール可能な状態だとステータスバーに重なってしまいました。
原因は、「binding.root」を指定していたからです。
Inflateしたレイアウトではなく、BottomSheetDialogFragment内部で設定されているViewに対してPaddingを設定します。

class MyBottomSheetDialogFragment : BottomSheetDialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val bottomSheetDialog = BottomSheetDialog(requireContext(), theme)
        
        _binding = FragmentMyBottomSheetBinding.inflate(LayoutInflater.from(context))
        bottomSheetDialog.setContentView(binding.root)

        with(bottomSheetDialog) {
            findViewById<View>(com.google.android.material.R.id.container)?.apply {
                ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
                    val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
                    view.updatePadding(top = bars.top)

                    WindowInsetsCompat.CONSUMED
                }
            }
        }
        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タイプごとに用意されているので、必要に応じて利用できます。

参考

いいなと思ったら応援しよう!