BottomSheetBehaviorを使った画面のEdgeToEdge対応

BottomSheetBehaviorを使った画面をAndroid 15（VanillaIceCream）のEdgeToEdge対応をしようとして苦労しました。
簡単に今となっては少しレガシーな感じのViewベースのレイアウトの画面の変化を見てみます

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffc0"
    >

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.MyApplication.AppBarOverlay"
        >

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            />
    </com.google.android.material.appbar.AppBarLayout>

    <FrameLayout
        android:id="@+id/sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ff0000"
        app:behavior_peekHeight="48dp"
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
        >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:gravity="center"
            android:text="BOTTOM SHEET"
            android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
            />
    </FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
```

Android 14までの場合は以下のように表示されます。ボトムシートを引き上げるとステータスバーの下まで引き上げることができます。

|||
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/36340d61-8027-dc9b-54f4-e660ce6f853f.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/6ae2460b-ae96-7996-02b0-e6b14cde3e3e.png)|

これをtargetSdk 35にしてAndroid 15で実行するとEdgeToEdgeが強制されるため以下のように表示されてしまいます。ボトムシート自体はWindowInsetsをうまく処理してくれているようで、ナビゲーションバーをよけた位置に表示してくれますが、Toolbarがステータスバーに食い込んでしまいます。

|||
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/2f51041c-9cfa-de4c-163a-8dcc29447e4d.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/ba0b67d4-2bb6-921b-6bb8-7bccaa629ce5.png)|

この例では、AppBarLayoutを使っているので、画面上部については`fitsSystemWindows="true"`を設定することで、AppBarLayoutにpaddingが設定されて画面内に収まるようになります。

```xml
<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.MyApplication.AppBarOverlay"
    android:fitsSystemWindows="true"
    >
```

|||
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/79cc3c43-1f79-c3aa-864c-3b3d6034316b.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/97c86e71-19f4-bb59-cc70-e510393a97ff.png)|


ボトムシートがステータスバーまで引き上げられてしまうので、上部にmarginを設定します。
marginを設定すると、BottomSheetBehaviorの制御位置がずれてしまうので、再設定も必要です。

```kotlin
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    binding.sheet.updateLayoutParams<MarginLayoutParams> {
        topMargin = systemBars.top
    }
    val behavior = (binding.sheet.layoutParams as CoordinatorLayout.LayoutParams)
        .behavior as BottomSheetBehavior
    behavior.state = BottomSheetBehavior.STATE_COLLAPSED
    insets
}
```

|||
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/814adcec-3c6e-28ec-6d74-b44d4e6420ee.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/d282e9b2-2557-c8cc-58ee-91fc68fd2d20.png)|

これで概ね対応ができました。

EdgeToEdgeですので、ナビゲーションバーの下にViewが潜り込んでいます。
レイアウトの都合でEdgeToEdgeだけどpaddingで埋めたい場合、注意が必要です。
BottomSheetBehaviorは親のCoordinatorLayoutのpaddingを無視した下部から、子Viewの高さ分までしか引き上げられません。padding分子ViewがClipされているので分かりにくいですが、CoordinatorLayoutに`android:clipToPadding="false"`を設定すると、子Viewが見えるようになります。
子Viewの高さはBehaviorが設定されていない場合の高さ、つまりCoordinatorLayoutのpadding分小さくなっていますので、以下のようにtop - bottomのマージンを設定することで調整します。

```kotlin
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    binding.sheet.updateLayoutParams<MarginLayoutParams> {
        topMargin = systemBars.top - systemBars.bottom
    }
    val behavior = (binding.sheet.layoutParams as CoordinatorLayout.LayoutParams)
        .behavior as BottomSheetBehavior
    behavior.state = BottomSheetBehavior.STATE_COLLAPSED
    v.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom)
    insets
}
```

|||
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/bd643f81-68e5-1d71-80f4-041b2b6683d5.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/79f5fb29-6ffc-bab1-e01b-d7a3988332c3.png)|

最初、AppBarLayoutで`fitsSystemWindows="true"`を使いましたが、これを使わずパディングを利用する場合、上下にpaddingを設定することになるので、どうようにmarginで調整します。幸いにもここでは負のマージンを設定することもできるので対応は簡単です。

```kotlin
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    binding.sheet.updateLayoutParams<MarginLayoutParams> {
        topMargin = -systemBars.bottom
    }
    val behavior = (binding.sheet.layoutParams as CoordinatorLayout.LayoutParams)
        .behavior as BottomSheetBehavior
    behavior.state = BottomSheetBehavior.STATE_COLLAPSED
    v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
    insets
}
```
|||
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/15e92b23-7176-2d3c-ffa9-e1263cfd8b5c.png)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/559cf6b8-baa0-1289-3fed-fee857753416.png)|

---
ポイントはCoordinatorLayoutのpaddingを無視した下部から、BottomSheetBehaviorを設定したViewの高さまでしか引き上げられないというところですね。これを知っていないとBottomのpaddingを設定した際に、意図した挙動にならず、調整に苦労してしまいます。

以上です。