BottomSheetをフルスクリーン状態で表示する方法

タイトルのような実装を行う機会がたまにあるのですが、毎回やり方が分からなくなっている気がするので、メモとして残しておきます。

結論から言うと、以下の対応をソースコード上で行うことによって実現できます。

  1. BottomSheetDialogコンテンツ表示部分のlayout_heightmatch_parentを指定する
  2. BottomSheetBehaviorpeekHeightを画面の縦幅と一致させる


具体的には以下のようになります。

class FullScreenBottomSheetDialogFragment : BottomSheetDialogFragment() {

  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val view = View.inflate(requireContext(), R.layout.fragment_full_screen_dialog, null)
        
    return super.onCreateDialog(savedInstanceState).apply {
      setContentView(view)
      setOnShowListener {
        val bottomSheetDialog = (it as? BottomSheetDialog) ?: return@setOnShowListener
        val bottomSheet = bottomSheetDialog.findViewById<FrameLayout>(R.id.design_bottom_sheet) ?: return@setOnShowListener

        // 1. BottomSheetDialogのコンテンツ表示部分のlayout_heightにmatch_parentを指定する
        bottomSheet.layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT

        // 2. BottomSheetBehaviorのpeekHeightを画面の縦幅と一致させる
        val screenHeight = Resources.getSystem().displayMetrics.heightPixels
        BottomSheetBehavior.from(bottomSheet).peekHeight = screenHeight
      }
    }
  }

  ...

}



ここからは、BottomSheet周辺の実装を追っていきます。

github.com


まず、1.の「BottomSheetDialogのコンテンツ表示部分のlayout_heightmatch_parentを指定する」に関連する実装を見ていきます。

BottomSheetDialogFragment#onCreateDialog()を呼び出すとBottomSheetDialogオブジェクトが返却されます。*1

@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
  return new BottomSheetDialog(getContext(), getTheme());
}


BottomSheetDialog#setContentView()の中でwrapInBottomSheet()が呼ばれ、その中でensureContainerAndBehavior()が呼ばれます。*2
ensureContainerAndBehavior()ではdesign_buttom_sheet_dialogの中のdesign_bottom_sheetBottomSheetBehaviorをセットする処理が行われます。

private FrameLayout ensureContainerAndBehavior() {
  if (container == null) {
    container = (FrameLayout) View.inflate(getContext(), R.layout.design_bottom_sheet_dialog, null);

    coordinator = (CoordinatorLayout) container.findViewById(R.id.coordinator);
    bottomSheet = (FrameLayout) container.findViewById(R.id.design_bottom_sheet);

    behavior = BottomSheetBehavior.from(bottomSheet);
    behavior.addBottomSheetCallback(bottomSheetCallback);
    behavior.setHideable(cancelable);
  }
  return container;
}


design_buttom_sheet_dialog.xmlを見ると、design_bottom_sheetlayout_heightwrap_contentが指定されています。*3

<FrameLayout
  android:id="@+id/design_bottom_sheet"
  style="?attr/bottomSheetStyle"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center_horizontal|top"
  app:layout_behavior="@string/bottom_sheet_behavior" />


したがって、design_bottom_sheetlayout_heightmatch_parentを指定することでレイアウトの表示領域を広げることができます。

次に、2.の「BottomSheetBehaviorpeekHeightを画面の縦幅と一致させる」に関連する実装を見ていきます。

BottomSheetDialog#onStart()ではBottomSheetBehaviorへstateをセットする処理が行われています。
stateのデフォルト値はSTATE_COLLAPSEDとなっているので、BottomSheetDialog#onStart()が呼ばれるまでにstateを明示的にSTATE_HIDDEN以外へ変更しない限り、縮小された状態でBottomSheetが表示されることになります。
(余談ですが、collapsedという単語の意味をいつも忘れてしまいます…笑)

@Override
protected void onStart() {
  super.onStart();
  if (behavior != null && behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
  }
}


「縮小された状態」とは、具体的には以下の状態です。*4

STATE_COLLAPSED:
The bottom sheet is visible but only showing its peek height.
This state is usually the ‘resting position’ of a Bottom Sheet.
The peek height is chosen by the developer and should be enough to indicate there is extra content, allow the user to trigger an action or expand the bottom sheet.


したがって、BottomSheetBehaviorpeekHeightに画面の縦幅を指定することでBottomSheetをフルスクリーン状態で表示できるようになります。

画面の縦幅の取得に関しては以下のサイトが個人的には分かりやすかったです。

akira-watson.com


以上となります。
個人的にはある程度の整理はできましたが、BottomSheet表示時のロジック(アニメーション含め)が今ひとつ理解できていないので、時間のあるときに改めて見ていきたいと思います。