Toolbarのサブタイトルをアニメーションする (CollapsingToolbarLayout subtitle)

CollapsingToolbarLayoutを使ってモダンなUIにしたい時でも、殆どの方は単体のToolbarも併用していると思います。しかし、タイトルをアニメーションさせたい時はCollapsingToolbarLayoutのタイトルを使わないと出来ませんし、サブタイトルに対応していません。

そこで、タイトルとサブタイトルはToolbarに任せたままで、スライドアニメーションさせてみます。

スクロールを検知してタイトルを拡大させるBehaviorを作る

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.example.myapplication.R
import com.google.android.material.appbar.AppBarLayout
import kotlin.math.abs
import kotlin.math.round

class CollapsingTitleBehavior(context: Context, attrs: AttributeSet): CoordinatorLayout.Behavior<ViewGroup>() {

    private var collapsedTitleSize = 0f
    private var collapsedSubtitleSize = 0f

    override fun layoutDependsOn(parent: CoordinatorLayout, child: ViewGroup, dependency: View): Boolean {
        return dependency is AppBarLayout
    }

    override fun onDependentViewChanged(parent: CoordinatorLayout, child: ViewGroup, dependency: View): Boolean {
        dependency as AppBarLayout

        val toolbar: Toolbar = dependency.findViewById(R.id.toolbar)
        val titleTextView = toolbar.getChildAt(0) as? TextView
        val subtitleTextView = toolbar.getChildAt(1) as? TextView

        if (collapsedTitleSize == 0f) {
            collapsedTitleSize = (titleTextView?.textSize ?: 0f) / toolbar.resources.displayMetrics.density
        }
        if (collapsedSubtitleSize == 0f) {
            collapsedSubtitleSize = (subtitleTextView?.textSize ?: 0f) / toolbar.resources.displayMetrics.density
        }

        val scale = {
            // 1.0 ~ 1.4
            val p = 1f + 0.4f * (1f - abs(dependency.y) / dependency.totalScrollRange)
            // Smooths the animation
            round(p * 100f) / 100f
        }()

        titleTextView?.textSize = collapsedTitleSize * scale
        subtitleTextView?.textSize = collapsedSubtitleSize * scale

        return true
    }
}

activity_main.xml

CoordinatorLayout直下に空のレイアウトを配置して先ほどのBehaviorを仕込むだけ。

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <!-- This will not be displayed -->
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_behavior="com.example.myapplication.CollapsingTitleBehavior" />

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        app:expanded="false">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="200dp"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:statusBarScrim="?attr/colorPrimary"
            app:titleEnabled="false">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:contentDescription="Logo"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"
                app:srcCompat="@drawable/ic_launcher_foreground" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                app:layout_collapseMode="pin"
                app:subtitle="サブタイトル"
                app:subtitleTextColor="@android:color/white"
                app:title="メインタイトル"
                app:titleTextColor="@android:color/white" />

        </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="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" />

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

拡大アニメーションがちょっとかくつきます。^^;;;

今回は拡大だけでしたが、文字色をレイアウト側で設定できるように改良すると実用性も上がると思います。