🔥

CoordinatorLayout + CollapsingToolbarLayoutでToolbarをスクロール時に開

2020/10/11に公開約6,700字

CoordinatorLayoutとMaterialDesignのCollapsingToolbarLayoutを使って、下のようにスクロール時にToolbarを開閉させる方法について書きます。

色が付いていると分かり辛いですが、閉じている状態ではelevationが適用され影が表示されます。白抜きにするとわかりやすいです。

ソースコード

app/build.gradle

dependencies {
   implementation 'com.google.android.material:material:1.2.1'
}

layout.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:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="112dp"
            android:fitsSystemWindows="true"
            android:minHeight="?actionBarSize"
            app:collapsedTitleTextAppearance="@style/AppTextAppearance.CollapsedTitle"
            app:expandedTitleGravity="start|bottom"
            app:expandedTitleMarginBottom="16dp"
            app:expandedTitleMarginStart="16dp"
            app:expandedTitleTextAppearance="@style/AppTextAppearance.ExpandedTitle"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <com.google.android.material.appbar.MaterialToolbar
                style="@style/AppToolbar"
                android:layout_width="match_parent"
                android:layout_height="?actionBarSize"
                app:layout_collapseMode="pin"
                app:navigationIcon="@drawable/ic_arrow_back"
                app:title="CollapsingToolbarLayout" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingStart="16dp"
            android:paddingEnd="16dp">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="24dp"
                android:text="Hello CollapsingToolbarLayout"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

style.xml

<resources>
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="colorOnPrimary">@color/white</item>
    </style>

    <style name="AppToolbar" parent="Widget.MaterialComponents.Toolbar.Primary">
        <item name="android:elevation">0dp</item>
        <item name="android:background">@android:color/transparent</item>
    </style>

    <style name="AppTextAppearance.ExpandedTitle" parent="TextAppearance.Design.CollapsingToolbar.Expanded">
        <item name="android:textColor">@color/white</item>
    </style>

    <style name="AppTextAppearance.CollapsedTitle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
        <item name="android:textColor">@color/white</item>
    </style>
</resources>

設定方法

ソースコードから重要な部分だけを抜き出すと以下のようになります。

<androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.appbar.AppBarLayout>

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_height="112dp"
            android:minHeight="?actionBarSize"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <com.google.android.material.appbar.MaterialToolbar
                android:layout_height="?actionBarSize"
                app:layout_collapseMode="pin" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

スクロールによる開閉の設定

  • 親レイアウトはCoordinatorLayout
  • スクロールにより動的に変わる箇所は AppBarLayout
  • スクロール箇所は NestedScrollView(または、RecyclerView)
    ※ 通常の ScrollView では動かないので注意

CoordinatorLayoutはスクロール操作を検知し、子Viewに伝える役割を持っています。

CollapsingToolbarLayout に app:layout_scrollFlags="scroll|exitUntilCollapsed" を設定することで、一番上までスクロールした時にのみAppBarLayoutの開閉が行われるようになります。

スクロール箇所は app:layout_behavior="@string/appbar_scrolling_view_behavior" を指定することでスクロールをCoordinatorLayoutに伝えるようになります。また、スクロール箇所の高さをAppBarLayout分、調整してくれます。

CollapsingToolbarLayoutの高さ

開いた状態(collapsed)の高さは CollapsingToolbarLayout の android:layout_height
閉じた状態(expanded)の高さは MaterialToolbar の android:layout_height

ここで注意しなければいけないのが CollapsingToolbarLayout の android:minHeight をツールバーの高さに合わせて設定している点。android:minHeight を設定しない、または、ツールバーの高さと差異がある場合、 開閉が動かなかったり、NestedScrollView がスクロールし切れずに画面下部が切れてしまいます。

ツールバーのピン留め

スクロールした時に MaterialToolbar のNavigationIconやメニューが移動しないように app:layout_collapseMode="pin" を設定してピン留めしている。
この設定がない場合、ツールバーのNavigationIconやメニューも移動の対象になります。

attributes

開閉時のテキスト属性の設定等できます。詳細は公式サイトへ。

https://material.io/develop/android/components/app-bars-top#contextual-action-bar

最後に

今回はシンプルにToolbarの開閉のみでしたが、FloatingButton等のView表示や画像を切り替えたりすることも可能です。

なお、今回の記事を書くに当たり下記サイトを参考にさせて頂きました。

Discussion

ログインするとコメントできます