It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Android】DataBinding+LiveData + ViewModelのHello World

概要

前回の記事では、LiveDataViewModelを使った基本的な数字カウントアップの実装を試しました。

inon29.hateblo.jp

今回は、前回のコードをベースにレイアウトにUIコンポーネントを直接紐付ける仕組みである DataBinding を追加してみます。

f:id:inon29:20200516191207p:plain

環境

  • compileSdkVersion: 29
  • Kotlin: v1.3.72
  • lifecycle-viewmodel-ktx: v2.2.0
  • lifecycle-livedata-ktx: v2.2.0

プロジェクトのセットアップ

まずは、DataBindingを使うためのプロジェクトのセットアップをします。
appディレクトリ配下のbuild.gradleに次の設定を追加します。

android {
    ・・・

    dataBinding {
        enabled true
    }
    
    ・・・
}

appディレクトリ配下の build.gradle にdataBindingについてのenable設定を記述します。

画面のレイアウト

前回の構成と同じ、数値を表示するTextViewとカウントアップするためのボタンを用意します。
(xmlについては、前回の記事を参照ください)

ViewModelとLiveDataの準備

ViewModelクラスについても前回の記事と変わりません。
(xmlについては、前回の記事を参照ください)

DataBindingのセットアップ①(レイアウトxml)

xmlで宣言しているレイアウトファイルにViewModelのデータをDataBindingを使って関連付けていきます。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        
        <variable
            name="viewModel"
            type="com.inon.apps.viewmodelsample.CountViewModel"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.text}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Count up"
            android:onClick="@{() -> viewModel.countUp()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

基本的な、画面構成は変わりませんが、ルートのタグが ConstraintLayout ではなく layout に変わっています。
このDataBindingを適用するためのxml構成については、Android Studioの機能で自動生成できます。
(alt + Enterで出てくるメニューから「Convert to data binding layout」を選択する)
詳しくは、Codelabsのサンプルがわかりやすいです。

<data>
    <variable
        name="viewModel"
        type="com.inon.apps.viewmodelsample.CountViewModel"/>
</data>

まずは、data タグを使って作成したViewModelクラスをxml上に宣言します。

<TextView
    ... 
    android:text="@{viewModel.text}"
    ... 

次に、TextViewに対して、ViewModelのプロパティで宣言しているLiveDataのtextを紐付けます。

<Button
    ... 
    android:onClick="@{() -> viewModel.countUp()}"
    ... 

最後に、カウントアップのアクションをButtonに紐付けます。

DataBindingのセットアップ②(Activity)

最後にActivityにDataBindingを使うための準備処理を実装します。

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.inon.apps.viewmodelsample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // NOTE: DataBindingUtilからBindingインスタンスを生成
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        // NOTE: ライフサイクルオーナにActivityを指定しないとデータの管理が始まらない
        binding.lifecycleOwner = this
        val viewModel = ViewModelProvider(this).get(CountViewModel::class.java)
        binding.viewModel = viewModel
    }
}

通常レイアウトを適用するには、 setContentView メソッドを使うかと思いますが、これを DataBindingUtil クラスが提供する setContentView に置き換えます。
このメソッドは戻り値としてDataBindingのインスタンスを返すのですが、ActivityMainBinding という今回作成していないクラスのインスタンスが返っています。
これは、xmlから自動生成されるDataBindingインスタンスです。(activity_main.xmlのためActivityMainBindingになっている)

DataBindingのインスタンスに対して、ライフサイクルのオーナーのインスタンス(Activityのインスタンス)を渡します。
そして、関連付けていたViewModelのインスタンスもDataBindingに紐付けます。

まとめ

DataBindingを使うと、LiveDataやそれに伴うイベントをxmlに宣言できるためActivityのコードは大分スッキリします。
基本的には、LiveData, ViewModel, DataBindingはセットで使うと良さそうです。

参考リンク