CollapsingToolbarLayout の状態変更を使いやすくする

はじめに

今回は、CollapsingToolbarLayout を使うときに便利なリスナーを紹介します。
CollapsingToolbarLayoutの基本的な実装については説明しないので、xmlの組み方については公式ページでも見てください。

material.io

f:id:QoopMk:20190926173140g:plain

AppBarLayoutをカスタム

CollapsingToolbarLayoutはAppBarLayout内で定義しますが、実際にCollapsingToolbarLayoutが閉じている状態(Collapse)なのか開いている状態(Expand)なのかということがわかりにくいです。

そこで以下のような Kotlin Extension を用意して、実装していきましょう。

/**
 * AppBarLayoutのスクロール状態
 */
sealed class AppBarLayoutScrollState {

    /**
     * スクロールされて閉じている状態
     */
    object Expanded : AppBarLayoutScrollState()

    /**
     * スクロールされて開いている状態
     */
    object Collapsed : AppBarLayoutScrollState()

    /**
     * スクロール途中の状態
     *
     * @property percentage スクロール比率(Expandedを1.0としてCollapsedを0.0とする)
     */
    data class Scrolling(
        @FloatRange(from = 0.0, to = 1.0) val percentage: Float
    ) : AppBarLayoutScrollState()
}

/**
 * AppBarLayoutのスクロール状態を監視するリスナーを設定
 *
 * @param listener リスナー
 */
fun AppBarLayout.setOnChangedAppBarScrollStateListener(listener: (AppBarLayoutScrollState) -> Unit) {
    addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
        when (abs(verticalOffset)) {
            0 -> {
                // Expanded
                listener(AppBarLayoutScrollState.Expanded)
                listener(AppBarLayoutScrollState.Scrolling(1f))
            }
            appBarLayout.totalScrollRange -> {
                // Collapsed
                listener(AppBarLayoutScrollState.Collapsed)
                listener(AppBarLayoutScrollState.Scrolling(0f))
            }
            else -> {
                // Scrolling
                val scrollPercentage =
                    1 - abs(verticalOffset / appBarLayout.totalScrollRange.toFloat())
                listener(AppBarLayoutScrollState.Scrolling(scrollPercentage))
            }
        }
    })
}

これを使用することで、
開閉に合わせてFABを表示・非表示を切り替えたり、
スクロールに合わせて、画像をフェードアウトさせたりということが可能になります!!

// AppBarLayout
appBarLayout.setOnChangedAppBarScrollStateListener {
    when (it) {
        is AppBarLayoutScrollState.Expanded -> {
            // スクロールで開いた時
            fab.show()
        }
        is AppBarLayoutScrollState.Collapsed -> {
            // スクロールで閉じた時
            fab.hide()
        }
        is AppBarLayoutScrollState.Scrolling -> {
            // スクロール中
            imageView.alpha = it.percentage
        }
    }
}