ViewModelで配列の状態変化を監視

今回はポンコツ2人組が「Jetpack Compose」のViewModelで状態変化の監視に挑戦します。 今回はViewModelでリストを監視していますが、これがKotlin&JetPack Compose未経験の2人組には非常に敷居が高く、さんざん試行錯誤した結果、 何とか動くようになりました。お願いだからもう少し分かりやすくして欲しいです・・・

※この記事は2023/09/09時点の情報です。

ViewModel
Jetpack Composeは、Androidアプリケーションのユーザーインターフェースを構築するためのモダンなUIツールキットです。 ViewModelは、UIの状態を管理し、データを保存・復元するためのJetpackライブラリの一部です。 画面の状態データを保持し、画面回転やアクティビティの再作成などの様々な状況でデータを保持できます。 また、ViewModelはUIコンポーネント(例えば、Composeの関数)と分離されているため、データのビジネスロジックとUIを結合にするのに役立ちます。 ViewModelを使用して状態変化を監視する手順は次の通りです。

1.クラスを作成
まず、ViewModelで管理するためのクラスを作成します。これは、UIの状態を管理するためのコンテナです。 通常ViewModelクラスを継承します。

2.Composable関数内でViewModelを取得
Composable関数内で、ViewModelを作成または取得します。
viewModel: MyViewModel = viewModel()

3.状態をViewModelに保存
UIの状態データ(例、テキスト、リスト、選択状態など)をViewModel内のプロパティに保存します。 これにより、状態がViewModelによって管理されるようになります。

4.ViewModelのデータを使用
Composable関数内で、ViewModelに保存されたデータを表示したり、操作したりします。 Composeの特徴であるデータ駆動型UIを活用します。

5.ViewModelのデータを監視:
ViewModel内のデータが変化するたびに、それを監視しUIを更新します。 Composeでは、observeAsState()関数を使用してViewModel内のデータを監視できます。

これで、ViewModelを使用して状態変化を監視し、ComposeでリアクティブなUIを構築できます。 ViewModelは、アプリケーションのデータ管理とUIの分離を支援し、アプリケーションの安定性と保守性を向上させます。

MainActivity
画面表示が終わったタイミングでデータクラスにデータをセットしてViewModelにセットしています。 Composeが状態変化を監視できているか確認したかったので画面表示後のタイミングにしています。 リストを監視する時のポイントはViewModelのクラスでMutableLiveData<MutableList<Sample>>(mutableStateListOf())にしていることです。 2人組がいろいろ試してみましたが、唯一成功したのがこれだけでした。(初心者には厳しかった・・・)

package com.example.samplepj2

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.clickable
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.ViewModel
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Text
import androidx.compose.runtime.livedata.observeAsState

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            // Composableを呼び出す
            SampleList()
            SampleList2()
        }
    }
}

// Sampleデータクラスを定義
data class Sample(val id: Int, val name: String)

class SampleViewModel : ViewModel() {
    private val sampleList = MutableLiveData<MutableList<Sample>>(mutableStateListOf())

    fun setSample(sample:MutableList<Sample>) {
        sampleList.value = sample
    }

    fun addSample() {
        val newSample = Sample((sampleList.value?.size ?: 0) + 1, "新しいサンプル")
        sampleList.value?.add(newSample)
    }

    fun getSample(): MutableLiveData<MutableList<Sample>> {
        return sampleList;
    }

    fun removeSample(sample: Sample) {
        sampleList.value?.remove(sample)
    }
}

@Composable
fun SampleList(viewModel: SampleViewModel = viewModel()) {
    val sampleList = viewModel.getSample().observeAsState()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        // データを表示
        sampleList.value?.forEach { sample ->
            Text(
                text = sample.name,
                modifier = Modifier
                    .padding(8.dp)
                    .clickable {
                        // リストアイテムをクリックしたときの処理
                        // ここではサンプルの削除を行います
                        viewModel.removeSample(sample)
                    }
            )
        }

        // ボタンを追加して新しいサンプルを追加する
        Button(
            onClick = {
                viewModel.addSample()
            },
            modifier = Modifier.padding(16.dp)
        ) {
            Text("新しいサンプルを追加")
        }
    }
}


@Composable
fun SampleList2(viewModel: SampleViewModel = viewModel()) {
    // 画面表示が終わったタイミングでデータを追加
    LaunchedEffect(Unit) {
        // 画面表示が終わったタイミングでデータを追加
        val initialData = mutableStateListOf(
            Sample(1, "サンプル1"),
            Sample(2, "サンプル2"),
            Sample(3, "サンプル3")
        )
        viewModel.setSample(initialData)
    }
}

@Preview
@Composable
fun SampleListPreview() {
    SampleList()
}

動作確認
今回のプログラムを実行した結果です。

「Jetpack Compose」のrememberとmutableStateListOf

いろいろなサイトを見て簡単に実装できるのかと余裕ぶっこいてましたが、 いざ実践してみると全然上手くいかず大苦戦でした・・・。この苦労は忘れないようにしよう。。。 ちなみにポンコツ女子はこんな↓感じです。頑張れ・・・。

「Jetpack Compose」のViewModel

管理人情報