2
2

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

<?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までの場合は以下のように表示されます。ボトムシートを引き上げるとステータスバーの下まで引き上げることができます。

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

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

<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"
    >

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

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
}

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

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

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
}

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

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
}

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

以上です。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2