[Android]BottomSheetの使い方

投稿者:

ある時期からスマホのUIに、画面下から「ニュッ」とせり出してくるメニューともダイアログともとれる動作が見られるようになった。このようなUIは、おそらくスマホの大画面化に伴い、片手で操作しやすいように、下の方に操作系を配置するためのものなのだと思う。

私は長らくこのUIを自分でも使いたいと思い、どうやってやるのだろうと調べていた。おそらくカスタムしたダイアログかカスタムFragmentであろうと「下から出てくる ダイアログ」といったワードで検索してみたが、そういったものを解説しているウェブページは見つからなかった。

しかし、こちらのページがきっかけで求めているものがBottomSheetという仕組みだとわかった。

BottomSheetはマテリアルデザインにおけるコンポーネントのひとつで「スクリーンの下からスライドアップして追加のコンテンツを表示する要素」 と定義されているらしい。ダイアログやメニューといった独立したオブジェクトだと思っていたけど、実は画面レイアウト上の仕組みだったのである。

そんなわけで、ここではBottomSheetの基本的な使い方を記しておく。

Gradle

Gradleに以下の記述を追加する。
後ろのバージョン番号はこの記事を書いている時点のものなので、最新のバージョンを指定すること。

dependencies {
    …
    implementation 'com.google.android.material:material:1.3.0'
    ….
}

レイアウト

BottomSheetとして扱うViewは必ずCoordinatorLayoutの直下に置かなければならない。そのうえで、そのViewがBottomSheetとしてふるまうことを属性で指定する。

app:layout_behavior

CoordinatorLayout直下のView(LinearLayout等)のlayout_behavior属性に 「android.support.design.widget.BottomSheetBehavior」を指定する。これにはシステムのstringリソースで「@string/bottom_sheet_behavior」という別名が付けられている。

app:behavior_peekHeight

縮小時の高さをbehavior_peekHeight属性で指定する。

app:behavior_hideable

下までスワイプした時にBottomSheetを非表示にするかどうかをbehavior_hideable属性で指定する。

<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">
    <!-- CoordinatorLayout直下のViewに -->
    <!-- app:layout_behavior="@string/bottom_sheet_behavior"を設定 -->
    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:behavior_peekHeight="50dp"
        app:behavior_hideable="false"
        app:layout_behavior="@string/bottom_sheet_behavior">

BottomSheetの操作

BottomSheetBhaviorの取得

レイアウトXMLで指定したBottmSheetの状態を取得したり、プログラムから操作するにはBottomSheetBehaviorのインスタンスを取得する

//Viewのインスタンス
LinearLayout bottomSheet = (LinearLayout)findViewById(R.id.bottom_sheet);
//BottomSheetBhaviorのインスタンス
BottomSheetBehavior<LinearLayout> behavior = BottomSheetBehavior.from(bottomSheet);

BottomSheetの状態

BottomSheetの状態はBottomSheetBehaviorのgetState()メソッドで取得することができる。
メソッドの戻り値は以下の通り

定数説明
STATE_DRAGGING1ボトムシートをつかんで移動開始
STATE_SETTLING2STATE_EXPANDED、STATE_COLLAPSEDへの遷移中
STATE_EXPANDED3全表示中
STATE_COLLAPSED4縮小化(peekHeightで指定した高さ)で表示中
STATE_HIDDEN5非表示中(画面下に隠れた状態)

BottomSheetの状態操作

BottomSheetの状態を変化させるにはBottomSheetBehaviorのsetState()メソッドで状態をセットする。

//BottomSheetの状態を展開状態にする
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);

BottomSheetの状態変化

BottomSheetBehaviorのaddBottomSheetCallbackで、BottomSheetが「全表示になった」「縮小化された」等、状態の変化を捉えることができる。

behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        //状態変化した後の処理
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        //スライド中の処理
    }
});

サンプルアプリ

以下にBottomSheetを使った簡単なサンプルを示す。

レイアウトファイル

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


    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="50sp"
        android:gravity="center_horizontal"/>

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@color/black"
        app:behavior_peekHeight="50dp"
        app:behavior_hideable="false"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <Button
            android:id="@+id/bottomsheet_switch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:background="@color/black"
            android:text="↑"
            android:textStyle="bold"
            android:textSize="20sp"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:foreground="@android:color/darker_gray"/>


        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@color/white"
            android:textSize="80sp"
            android:text="Goodbye World"/>

    </LinearLayout>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

JAVAファイル

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout bottomSheet = (LinearLayout)findViewById(R.id.bottom_sheet);
        final Button bsSwitch = (Button)findViewById(R.id.bottomsheet_switch);
        final BottomSheetBehavior<LinearLayout> behavior = BottomSheetBehavior.from(bottomSheet);

        behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                //状態が変化したらボタンのテキストを変える
                switch (newState) {
                    case BottomSheetBehavior.STATE_COLLAPSED:
                        bsSwitch.setText("↑");
                        break;
                    case BottomSheetBehavior.STATE_EXPANDED:
                        bsSwitch.setText("↓");
                        break;
                }
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
            }
        });

        bsSwitch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //ボタンを押したときの動作
                switch (behavior.getState()) {
                    case BottomSheetBehavior.STATE_EXPANDED:
                        behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                        break;
                    case BottomSheetBehavior.STATE_COLLAPSED:
                        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                        break;
                    default:
                        break;
                }
            }
        });
    }
}

このアプリを起動すると下のような画面が表示される。

初期状態
BottomSheetを展開

下の黒い帯を上にスワイプするとBottomSheetが展開され「GoodBye World」とテキストが表示され、ここから下にスワイプすると縮小状態に戻る。「↑」はボタンで、これをタップしてもBottomSheet展開され、再度ボタンをタップすると縮小状態に戻る。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください