这里是 MainViewModel.kt:
class MainViewModel : ViewModel() {
var fruit by mutableStateOf("") // changed only by changeFruit() method
private set
/** Validates and changes [fruit] and returns the result. */
fun changeFruit(value: String): String {
fruit = if (value == "apple") "banana" else value
return fruit
}
// ...other methods that can change the fruit variable with changeFruit()...
}
这里是 MainActivity.kt:
import ...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Tests5Theme {
App()
}
}
}
}
@Composable
fun App() {
val vm: MainViewModel = viewModel()
Column {
var fieldValue by remember { mutableStateOf(vm.fruit) }
Field(
value = fieldValue,
onValueChange = { fieldValue = it },
onDone = { vm.changeFruit(fieldValue) }
)
// I know onClick could be { fieldValue = vm.changeFruit("apple") }
// and { fieldValue = vm.changeFruit("strawberry") }
// but what if these buttons are placed away in other composables?
Button(onClick = { vm.changeFruit("apple") }) {
Text(text = "Change fruit to apple")
}
Button(onClick = { vm.changeFruit("strawberry") }) {
Text(text = "Change fruit to strawberry")
}
Text(text = "Value of MainViewModel.fruit: ${vm.fruit}")
}
}
/**
* When the done button of the keyboard is pressed, the focus is cleared from the field, then the onFocusChanged()
* modifier is called, and then onDone() is called.
*
* @param value the value shown in the field.
* @param onValueChange the lambda called when the field value is changed.
* @param onDone the lambda called when the done button of the keyboard is pressed or the focus goes away.
*/
@Composable
fun Field(
value: String,
onValueChange: (String) -> Unit,
onDone: () -> Unit
) {
val focusManager = LocalFocusManager.current
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier.onFocusChanged {
if (!it.isFocused) onDone()
},
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus() }
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
)
}
fruit
的值可以在文本字段中键入、按下按钮或使用 Viewmodel 中的其他方法进行更改。在调用fieldValue
函数后,我无法弄清楚如何使fruit
(textField的值)等于changeFruit()
变量。
在此先感谢帮助我的人。
fruit
已经是保存在 viewModel
中的状态,这里的问题是您正在使用 remember
初始化另一个状态。请记住,通过组合生命周期保存值,因此当在 fieldValue
lambda 中设置 onValueChange = { fieldValue = it }
时,您的视图会更新,因为设置状态会触发重新组合,但是当您以编程方式设置它时它不会更新,因为没有任何东西可以触发重新组合.有两种解决方案:
1.你可以直接使用
viewModel
中的状态,不要用remember创建另一个冗余状态。
@Composable
fun App() {
val vm: MainViewModel = viewModel()
Column {
Field(
value = vm.fruit,
onValueChange = { vm.changeFruit(it) }
)
}
}
2.将
key
传递给remember
会强制它重新计算它存储的值,这也会触发重组
@Composable
fun App() {
val vm: MainViewModel = viewModel()
var fieldValue by remember(key1 = vm.fruit) { mutableStateOf(vm.fruit) }
Column {
Field(
value = fieldValue,
onValueChange = { vm.changeFruit(it) }
)
}
}
有关性能的附加信息,将
value
直接传递给您的 Composable 将导致整个父 Composable 中的重组,在您的情况下为 App()
,因为重组将在其范围内有 state read 时开始,这意味着如果其他可组合项不是 skippable
.,则对它们进行冗余重组
@Composable
fun Field(
provideValue: () -> String,
onValueChange: (String) -> Unit
) {
TextField(
value = provideValue(),
onValueChange = onValueChange
)
}
@Composable
fun App() {
Field(
provideValue = { vm.fruit },
onValueChange = { vm.changeFruit(it) }
)
}
这种状态将在
provideValue()
lambda 的范围内读取,而 不在父可组合项 中,因此重组只会发生在 Field()
可组合项中。这称为 lambda 方法,建议使用它来传递频繁变化的状态。