Overview
Toolbarの下のScrollViewやRecyclerViewをスクロールした時にToolbarの影をつけたい時の実装です。
ToolbarのStateListAnimator
と、ScrollViewのcanScrollVertically()
を使って実現します。
1. ObjectAnimatorを作る
状態がselectedになった時にelevationを変化させるObjectAnimatorを作ります。
res/animator/toolbar_elevation.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<objectAnimator
android:duration="200"
android:propertyName="elevation"
android:valueTo="4dp"
android:valueType="floatType" />
</item>
<item>
<objectAnimator
android:duration="200"
android:propertyName="elevation"
android:valueTo="0dp"
android:valueType="floatType" />
</item>
</selector>
2. ToolbarにObjectAnimatorをセットする
作成したObjectAnimatorをToolbarにセットします。
res/layout/{layout_name}.xml
<androidx.appcompat.widget.Toolbar
...
android:stateListAnimator="@animator/toolbar_elevation" />
3. Toolbarの状態を変更する
ScrollView/RecyclerViewのcanScrollVertically()の値でToolbarの状態を変更します。
ActivityorFragment.kt
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
// -1をセットすると、スクロール位置が一番上の時のみtrueになる
isSelected = binding.scrollView.canScrollVertically(-1)
}
これでスクロール時にToolbarの選択状態が変わり、elevationが変化するようになります。
DataBinding Tips
styleとDataBindingのCustomBindingAdapterを使って共通化することもできます。
res/values/styles.xml
<style name="Toolbar.NoShadow" parent="Widget.AppCompat.Toolbar">
<item name="android:elevation">0dp</item>
<item name="android:stateListAnimator">@animator/toolbar_elevation</item>
</style>
ToolbarBindingAdapters.kt
@BindingAdapter("shadowAnimationScrollViewId")
fun Toolbar.setShadowAnimationScrollViewId(@IdRes scrollViewId: Int) {
val scrollView = this.rootView.findViewById<View>(scrollViewId)
if (scrollView is ScrollView) {
scrollView.viewTreeObserver.addOnScrollChangedListener {
isSelected = scrollView.canScrollVertically(-1)
}
}
}
layout/{layout}.xml
<android.support.v7.widget.Toolbar
...
style="@style/Toolbar.NoShadow"
app:shadowAnimationScrollViewId="@{R.id.scroll}" />
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
</ScrollView>