ViewModelとかLiveData
よくわからない。ミームとかじゃなくてほんとによくわからない。特に益になるような事は書いてない。
サンプル
build.gradleに
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
を追記。
のを参考に、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に配列を使いたい
で、これを配列で使いたいの。でもね、公式に思いっきし
注意: 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<*>)’ method. None of the following functions is suitable:<br/>public inline operator fun <T> State<String>.getValue(thisObj: Any?, property: KProperty<*>): 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) -> Unit, modifier: Modifier = …, enabled: Boolean = …, readOnly: Boolean = …, textStyle: TextStyle = …, label: (() -> Unit)? = …, placeholder: (() -> Unit)? = …, leadingIcon: (() -> Unit)? = …, trailingIcon: (() -> 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) -> Unit, modifier: Modifier = …, enabled: Boolean = …, readOnly: Boolean = …, textStyle: TextStyle = …, label: (() -> Unit)? = …, placeholder: (() -> Unit)? = …, leadingIcon: (() -> Unit)? = …, trailingIcon: (() -> 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()
などのオブザーバブルなデータホルダーを使用することをおすすめします。
これだ。がっくし。
コメント