[Kotlin]MVVMモデルの構築の仕方

アイキャッチ画像 kotlin

初めに

この記事は[kotlin]MVVMモデルとはの続きです。
MVVMモデルの概要について知りたいという方はこちらの記事からご覧ください。
この記事では上記の記事に記載してあるサンプルコードの解説を行います。

Modelの部分について

// Model
data class User(val name: String, val age: Int)

このMVVMモデルにおけるModelの役割は非常に単純です。
data classというデータを保持するためだけのクラスに「名前」・「年齢」の二つの要素を持つUserを定義しています。

ViewModelの部分について

// ViewModel
class UserViewModel : ViewModel() {
    private var _user = mutableStateOf(User("John Doe", 30))
    val user: State<User> get() = _user

    fun updateUser(name: String, age: Int) {
        viewModelScope.launch {
            _user.value = User(name, age)
        }
    }
}

ViewModelはまず、このクラスはViewModelですよ。ということを、明示的に定義しないといけません。
それがClass名の後ろについている、 : ViewModel()でViewModelというクラスを継承しています。
このようにViewModel()をつけることにより、UserViewModelをViewModelとして使えるようになります。
そして、ViewModelで最も特徴的なのが、

private var _user = mutableStateOf(User("John Doe", 30))
val user: State<User> get() = _user

上記の部分だと思います。
_userとuserを二種類定義しています。

まず、mutableStateOf型の_userを定義します。
mutableStateOfとは、変数の内容に変更があった時に即座にUIに反映させることができる型です。
今回のサンプルコードではUserの内容をUIに反映させる必要があるので、この部分でUI(View)の部分とViewModelが相互に作用しあっています。
そしてuserというState<User>型のパブリックプロパティを定義します。
get()をつけることにより、_userの中身が変更されるたびに自動的にuserの値も更新されます。

なぜ_userとuserを分けているのか?

これはMVVMモデルがModel↔ViewModel↔Viewとそれぞれ隣接しているコンポーネントのみに影響を及ぼすことを前提としているからです。
_userとuserを分けることによって、Viewで更新されたUserの内容をViewが直接、Modelに影響を及ぼすことなく、ViewModelに定義されている_userを介して、通知できるようになります。

Viewの部分について

// View
@Composable
fun UserView(viewModel: UserViewModel) {
    val user by viewModel.user.collectAsState()

    var newName by remember { mutableStateOf("") }
    var newAge by remember { mutableStateOf("") }

    Column {
        Text(text = "Name: ${user.name}")
        Text(text = "Age: ${user.age}")
        TextField(value = newName, onValueChange = { newName = it }, label = { Text("New Name") })
        TextField(value = newAge, onValueChange = { newAge = it }, label = { Text("New Age") })
        Button(onClick = { viewModel.updateUser(newName, newAge.toInt()) }) {
            Text("Update User")
        }
    }
}

@Composableとは

これはアノテーションといって注釈のようなものです。
@Composableと関数に注釈をつけることによって、その関数がコンポーザブル関数であるということを示しています。
コンポーザブル関数となることにより、UIを描画する用の関数として利用できるようになります。

viewModel: UserViewModelとは

引数の部分でviewModel: UserViewModelと指定することによりUserViewModelに定義されているインスタンスを利用できるようになります。
実際に利用するときはviewModel.userのようにカンマで区切ってUserViewModelに定義されている要素をします。

collectAsState()とは

collectAsState()とは、その値が変更されると自動的にUIのも変更されるようにするためのKotlinの関数です。

rememberとは

rememberとは、コンポーザブルが再描画されてもその値を保持しておくためのJetpackComposeの関数です。他のUIが変更されたときに、コンポーザブルが再描画されてもその値が初期化されないように状態を維持するために利用します。

onValueChangeとは

onValueChangeとはテキストフィールドのプロパティです。
テキストフィールドの値が更新されたときに呼び出されるコールバック関数を指定します。
つまり、テキストフィールドに値が入力されると、その値をnewNameに保存するようになっています。

まとめ

今回のサンプルコードはUIに表示された名前・年齢の入力するテキストフィールドにそれぞれの値を入力すると、その変更がViewModelに伝えられ、それがModelに伝えられます。
そして、その入力されたデータが反映されるとViewModelからViewに変更が伝えられるので、同時に表示される値も変わります。
順序としては
①Viewで値の入力を感知
②ViewModelに入力された値が伝わる
③ViewModelが入力された値をModelに伝える
④ViewModelに定義されているuserの値が変わる
⑤Viewがuserの値の変更を受け取る
⑥UIが再描画される
このような順番でプログラムが進行しています。