Android
DataBinding

ToolbarをDataBindingで扱うTips

Overview

DataBindingを使ってToolbarを扱う時のTipsをまとめます。
ListenerやCustom BindingAdaptersを使うので好みもあると思いますが、ViewModelやPresenterクラスをbindして実装する場合には便利なのでどんなことができるのかをざっと知っておきましょう。

メニューの制御

Toolbar上のメニューをinflateし、タップしたときのイベントをハンドリングします。
inflateMenuResIdというBindingAdapterを作って実現します。

ToolbarBindingAdapters.kt
@BindingAdapter("inflateMenuResId")
fun Toolbar.setInflateMenu(@MenuRes menuResId: Int?) {
    if (menu.isEmpty() && menuResId != null && menuResId != 0) {
        inflateMenu(menuResId)
    }
}
layout/{layout}.xml
<layout>
    <data>
        <import type="com.konifar.sample.R" />

                <variable
            name="viewModel"
            type="com.konifar.sample.SampleViewModel" />
    </data>
    ...

        <androidx.appcompat.widget.Toolbar
            ...
            app:inflateMenuResId="@{R.menu.sample_menu}"
            app:onMenuItemClick="@{viewModel::onMenuItemClick}" />
SampleViewModel.kt
fun onMenuItemClick(@Suppress("UNUSED_PARAMETER") menu: MenuItem): Boolean {
    // メニューがタップされた時の処理
    return true
}

ViewModelにmenuResIdのようなIntの値を持ってバインドすることで、動的にメニューを変更することもできます。

Navigationアイコンのタップ制御

Toolbar左上のアイコンを変更したり、タップしたときのイベントをハンドリングすることもできます。

layout/{layout}.xml
<layout>
    <data>
        <import type="androidx.core.content.ContextCompat" />

                <variable
            name="viewModel"
            type="com.konifar.sample.SampleViewModel" />
    </data>
    ...

        <androidx.appcompat.widget.Toolbar
            app:navigationIcon="@{ContextCompat.getDrawable(context, viewModel.navigationIconResId)}"
            app:navigationOnClickListener="@{viewModel::onNavigationClick}" />
SampleViewModel.kt
// 動的に変更する場合はObservableIntなどを使う
val navigationIconResId = R.drawable.back

fun onNavigationClick(@Suppress("UNUSED_PARAMETER") view: View) {
    // タップした時の実装
}

スクロール時のelevation変更

スクロールした時にToolbarに影をつけるための実装をDataBindingで共通化できます。
詳しくはこちら => https://qiita.com/konifar/items/4a5568b32f2ced664706#databinding-tips

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
<androidx.appcompat.widget.Toolbar
    ...
    android:stateListAnimator="@animator/toolbar_elevation"
    app:shadowAnimationScrollViewId="@{R.id.scroll}" />

<ScrollView
    android:id="@+id/scroll"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    ...
</ScrollView>

DataBindingで共通化することで、xmlだけで簡単に画面を作れるようになるだけでなく、ViewModelでUnitTestが書きやすくなるというメリットもあります。プロジェクトやチームの様子を見ながら取り入れてみてください。

以上です。