複数の要素をAppBarLayoutを使ってスクロール時に収納する

この記事は Goodpatch Advent Calender 2019 の 20日目です

Androidのアプリでたまに見かけるモダンなスクロールを作っており、
少し苦労したので、その知見を共有したいと思い書きました

# やったもの
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67480/9ce6fa88-23f4-b4eb-0af7-3ebe39afd96a.gif" width="300">

スクロールすると上にタイトルが固定表示されるような画面で、
１つの要素ではなく複数の要素で構成したい場合に以下の方法を使って実現しました。

# 実装方法
この実装にはレイアウトで対応するのが難しく、コードで制御するようにしました。

## レイアウトの構成
```
<com.google.android.material.appbar.AppBarLayout
    android:id="@+id/mainAppBar"
    android:layout_width="match_parent"
    android:layout_height="260dp"
    android:background="@color/colorWhite"
    android:fitsSystemWindows="true">
            
    <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="@android:color/transparent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:title="@string/mission"
            app:expandedTitleMarginTop="24dp"
            app:expandedTitleMarginStart="16dp"
            app:collapsedTitleTextAppearance="@style/TextAppearance.Title.Collapsed"
            app:expandedTitleTextAppearance="@style/TextAppearance.Title.Expanded"
    >

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:gravity="center_vertical|start"
                android:alpha="0"
                app:layout_collapseMode="pin"
                app:contentInsetStartWithNavigation="0dp"
                app:contentInsetStart="0dp"
                app:navigationIcon="@drawable/ic_arrow_back_black_24dp"
        >
        </androidx.appcompat.widget.Toolbar>

        <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/collapsingContents"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/transparent"
                app:layout_collapseMode="parallax"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:layout_behavior="com.package.view.HidingBehavior"
        >

            <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/button_expand_back"
                    android:layout_width="24dp"
                    android:layout_height="24dp"
                    android:layout_margin="16dp"
                    app:srcCompat="@drawable/ic_arrow_back_black_24dp"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
            />

            <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/mission"
                    android:layout_width="256dp"
                    android:layout_height="wrap_content"
                    android:textSize="30sp"
                    android:text="@string/vision"
                    android:textColor="@android:color/black"
                    android:layout_marginTop="8dp"
                    android:layout_marginStart="16dp"
                    app:layout_constraintTop_toBottomOf="@id/button_expand_back"
                    app:layout_constraintStart_toStartOf="parent"
            />

            <androidx.appcompat.widget.AppCompatImageView
                    android:layout_width="100dp"
                    android:layout_height="100dp"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    android:scaleType="fitEnd"
                    app:srcCompat="@drawable/girls_guns"

            />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
```

## ポイントとなるコード
```
binding.mainAppBar.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
     override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) {
          val container = binding.collapsingContents
          val toolbar = binding.toolbar
          appBarLayout?.let {
               if (verticalOffset == 0) {
                   if (state != CollapsingToolbarLayoutState.EXPANDED) {
                       state = CollapsingToolbarLayoutState.EXPANDED
                       container.alpha = 1f
                       toolbar.alpha = 0f
                   }
               } else if (Math.abs(verticalOffset) >= it.totalScrollRange) {
                   if (state != CollapsingToolbarLayoutState.COLLAPSED) {
                            state = CollapsingToolbarLayoutState.COLLAPSED
                            container.alpha = 0f
                            toolbar.alpha = 1f
                        }
                   } else {
                        if (state == CollapsingToolbarLayoutState.COLLAPSED) {
                            container.alpha = 0.5f
                            toolbar.alpha = 0f
                        }
                        state = CollapsingToolbarLayoutState.INTERNEDIATE
               }
           }
       }
})

private var state: CollapsingToolbarLayoutState = CollapsingToolbarLayoutState.EXPANDED

private enum class CollapsingToolbarLayoutState {
     EXPANDED,
     COLLAPSED,
     INTERNEDIATE
}
```


# さいごに
今回作ったコードを公開しました
気になるところがありましたら連絡お願いします
https://github.com/hayato-k/CollspanToolbarLayout