It’s now or never

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

【Android】LiveData + ViewModelのHello World

概要

Android Jetpack に含まれる「LiveData」についての入門になります。
Jetpackが発表されて約2年ほど立ちますが、この間ほとんどAndroid開発は触っていなかったため、かなり置いていかれてしまいました。
最新のAndroid開発事情に追いつくためにもまずはLiveData,ViewModel周りから触っていきたいと思います。

HelloWoldとして、ボタンを押下したら画面上の数字カウンターをインクリメントしていくよくあるサンプルをViewModel + LiveDataを使って実装します。

f:id:inon29:20200516191207p:plain

環境

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

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

appディレクトリ配下のbuild.gradleに以下を追加する

dependencies {
・・・
    /* ViewModel */
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
    /* LiveData */
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
・・・
}

appディレクトリ配下の build.gradle にViewModelとLiveDataのモジュールインポートを宣言します。
(今までは、lifecycle-extensionsというオールインワンのモジュールがあったようですが、2.2.0でサポートが終わるようなので、今後は個別のモジュールでインポートするのが良さそうです。)

画面のレイアウト

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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="count: 0"
        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"
        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>

ここは特別な設定などはありません。
カウントを表示するための TextViewとカウントアップのための Button を用意しています。

ViewModelとLiveDataの準備

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class CountViewModel : ViewModel() {
    // NOTE: TextViewに表示するテキスト用のLiveData.
    // 変更可のためMutableLiveDataで作成する
    var text = MutableLiveData<String>()
    // NOTE: カウント管理用の変数
    private var count = 0

    // カウントアップボタンを押下されたときのイベントを受け取るためのメソッド
    fun countUp() {
        count += 1
        text.value = "count $count"
    }
}

ViewModelクラスを継承したカウントアップの値を保持する独自のViewModelを作成します。 TextViewに設定するためのtextを LiveData として用意します。
今回は、変更可能なデータとして扱いたいため MutableLiveData を使用しています。

ViewModelのセットアップ

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // NOTE: ViewModelの生成
        val viewModel = ViewModelProvider(this).get(CountViewModel::class.java)
        // NOTE: LiveDataの値の変更を監視。変更を受け取ったらTextViewに値をセット。
        viewModel.text.observe(this,
            Observer { resource -> findViewById<TextView>(R.id.textView).text = resource }
        )
        // NOTE: ボタンのクリックリスナーの設定
        findViewById<TextView>(R.id.button).setOnClickListener{ viewModel.countUp() }
    }
}

最後にActivityでViewModelを生成します。
ViewModelのインスタンスは、ViewModelPloviderを使って作成します。
ViewModelPloviderには、Activityを引数として渡す必要があります。

生成されたViewModelインスタンスのtext LiveDataに対して、observe メソッドを使うことで値の変更を監視することができます。
ここに渡した Observerクロージャに変更した値が送られるため、その値とTextViewを紐付けることでリアクティブなデータ更新ができるようになります。

まとめ

基本的な使い方としてはシンプルでコードもスッキリしそうです。
LiveData+ViewModelは、基本的にはDataBindingと組み合わせて使うことが多そうなので、そのへんも次回は基本から調べて行こうと思います。