ViewModelとかLiveDataとかStateとか全然わからない

スポンサーリンク
スポンサーリンク

ViewModelとかLiveData

よくわからない。ミームとかじゃなくてほんとによくわからない。特に益になるような事は書いてない。

スポンサーリンク

サンプル

build.gradleに

implementation "androidx.compose.runtime:runtime-livedata:$compose_version"

を追記。

State and Jetpack Compose  |  Android Developers

のを参考に、MainActivity.ktを

@Composable
fun HelloScreen(jcViewModel: JetComViewModel = JetComViewModel()) {
    val name: String by jcViewModel.name.observeAsState("")
    HelloContent(name = name, onNameChange = { jcViewModel.onNameChange(it) })
}

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = onNameChange,
            label = { Text("Name") }
        )
    }
}

そんでJetComViewModel.ktを

class JetComViewModel : ViewModel() {

    private val _name = MutableLiveData("")
    val name: LiveData<String> = _name

    fun onNameChange(newName: String) {
        _name.value = newName
    }
    
}

クラス名しか変えてない。

ちゃんと動く。

ViewModel内で

private val _name = MutableLiveData("")

MutableLiveDataの変数を内部で用意。

val name: LiveData<String> = _name

LiveDataで外部に公開。

Mainで

val name: String by jcViewModel.name.observeAsState("")

で監視する。

OutlinedTextFieldで文字を入力すると

OutlinedTextField(
    value = name,
    onValueChange = onNameChange,
    label = { Text("Name") }
)

onValueChangeが呼ばれる。ここではonNameChangeが実行される。onNameChangeは

fun HelloContent(name: String, onNameChange: (String) -> Unit) {

この関数の引数で

HelloContent(name = name, onNameChange = { jcViewModel.onNameChange(it) })

呼び出し時にviewmodelのonNameChangeが実行される。viewmodelのonNameChangeは

fun onNameChange(newName: String) {
    _name.value = newName
}

渡された値で

private val _name = MutableLiveData("")

と定義された_nameへ代入して更新する。_nameは

val name: LiveData<String> = _name

なのでつまりnameが変更を検知?するのかな?で、nameが更新したので

HelloContent(name = name, onNameChange = { jcViewModel.onNameChange(it) })

nameを受け取って

Text(
    text = "Hello, $name",
    modifier = Modifier.padding(bottom = 8.dp),
    style = MaterialTheme.typography.h5
)

表示する。こんな感じなんかなー。

スポンサーリンク

MutableLiveDataに配列を使いたい

で、これを配列で使いたいの。でもね、公式に思いっきし

State and Jetpack Compose  |  Android Developers

注意: Compose の ArrayList<T> や mutableListOf() のような可変オブジェクトを状態として使用すると、アプリで誤ったデータや古いデータがユーザーに表示される現象が発生します。

ArrayList<T> や可変データクラスなどの非オブザーバブルな可変オブジェクトは、Compose による観測が不可能であり、変更されると再コンポジションがトリガーされます。

非オブザーバブルな可変オブジェクトを使用する代わりに、State<List<T>> や不変の listOf() などのオブザーバブルなデータホルダーを使用することをおすすめします。

やめとけ、と書かれている。でもしたい。。。

変えてみてはエラーメッセージを読み、を繰り返そう。

private val _name = MutableLiveData(mutableListOf("","",""))

て、要素数3のにしたら

val name: LiveData<String> = _name

Type mismatch: inferred type is MutableLiveData<MutableList<String>!> but LiveData<String> was expected

だけど

val name: LiveData<MutableList<String>> = _name

こうしてみる。すると

fun onNameChange(newName: String) {
    _name.value = newName
}

Type mismatch: inferred type is String but MutableList<String>? was expected

まこれはそうよね。これはちょっと置いといて。

Mainの方で

val name: String by jcViewModel.name.observeAsState("")

Property delegate must have a ‘getValue(Nothing?, KProperty&lt;*&gt;)’ method. None of the following functions is suitable:<br/>public inline operator fun &lt;T&gt; State&lt;String&gt;.getValue(thisObj: Any?, property: KProperty&lt;*&gt;): String defined in androidx.compose.runtime

いろいろ試して

val name: MutableList<String> by jcViewModel.name.observeAsState(mutableListOf("","",""))

こうだとエラーでない。

HelloContent(name = name, onNameChange = { jcViewModel.onNameChange(it) })

Type mismatch: inferred type is MutableList<String> but String was expected

なので

fun HelloContent(name :MutableList<String>, onNameChange: (String) -> Unit) {

こう変えてみる。そしたら

OutlinedTextField(
    value = name,
    onValueChange = onNameChange,
    label = { Text("Name") }
)

これが

None of the following functions can be called with the arguments supplied:<br/>public fun OutlinedTextField(value: TextFieldValue, onValueChange: (TextFieldValue) -&gt; Unit, modifier: Modifier = …, enabled: Boolean = …, readOnly: Boolean = …, textStyle: TextStyle = …, label: (() -&gt; Unit)? = …, placeholder: (() -&gt; Unit)? = …, leadingIcon: (() -&gt; Unit)? = …, trailingIcon: (() -&gt; Unit)? = …, isError: Boolean = …, visualTransformation: VisualTransformation = …, keyboardOptions: KeyboardOptions = …, keyboardActions: KeyboardActions = …, singleLine: Boolean = …, maxLines: Int = …, interactionSource: MutableInteractionSource = …, colors: TextFieldColors = …): Unit defined in androidx.compose.material<br/>public fun OutlinedTextField(value: String, onValueChange: (String) -&gt; Unit, modifier: Modifier = …, enabled: Boolean = …, readOnly: Boolean = …, textStyle: TextStyle = …, label: (() -&gt; Unit)? = …, placeholder: (() -&gt; Unit)? = …, leadingIcon: (() -&gt; Unit)? = …, trailingIcon: (() -&gt; Unit)? = …, isError: Boolean = …, visualTransformation: VisualTransformation = …, keyboardOptions: KeyboardOptions = …, keyboardActions: KeyboardActions = …, singleLine: Boolean = …, maxLines: Int = …, interactionSource: MutableInteractionSource = …, colors: TextFieldColors = …): Unit defined in androidx.compose.material

@Composable invocations can only happen from the context of a @Composable function

そしたら

value = name[0],

こう変えた。Mainの方はエラー表示されなくなった。

じゃあ上でほっといたviewmodelの

fun onNameChange(newName: String) {
    _name.value = newName
}

_name.value[0] = newName

こうしたら

Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type MutableList<String>?

なので

_name.value!![0] = newName

こうしてみた。一応これでエラーは表示されなくなったけど…

動かしてみると

うまく動かない。つまりは

注意: Compose の ArrayList<T> や mutableListOf() のような可変オブジェクトを状態として使用すると、アプリで誤ったデータや古いデータがユーザーに表示される現象が発生します。

ArrayList<T> や可変データクラスなどの非オブザーバブルな可変オブジェクトは、Compose による観測が不可能であり、変更されると再コンポジションがトリガーされます。

非オブザーバブルな可変オブジェクトを使用する代わりに、State<List<T>> や不変の listOf() などのオブザーバブルなデータホルダーを使用することをおすすめします。

これだ。がっくし。

スポンサーリンク

参考

State and Jetpack Compose  |  Android Developers
LiveData  |  Android Developers
LiveData Overview  |  Android Developers
Use LiveData to handle data in a lifecycle-aware fashion.
Compose 和其他库  |  Jetpack Compose  |  Android Developers
androidx.compose.runtime.livedata  |  Android 开发者  |  Android Developers
Notify Observer when item is added to List of LiveData
I need to get an Observer event when the item is added to the List of LiveData. But as far as I understand the event receives only when I replace the old list w...
AndroidのDataBindingで配列などを参照で変更するとき|teratail
Android開発でMVVM実装を使っています。 通常、LiveDataで配列(List)などを持っているとき、その配列の一部の値を変更したい場合、まず新しく配列を生成して、その配列の一部の値を変更して、その新しい配列をsetすると思います。 しかし、パフォーマンス的な要因で、毎回新しく配列を生

コメント

タイトルとURLをコピーしました