我正在尝试在 Jetpack Compose 中测试 ViewModel 概念,我一直被这个问题难住了,它让我花了一整天时间无法弄清楚。调试应用程序时,我看到 ViewModel 实际上正在更新,但它不会导致我的 UI 在更新后重新组合。
作为对这个概念的更小的测试,我创建了一个应用程序,实际上只有一个文本和按钮元素可组合。单击该按钮应该会在更新 ViewModel 后更改文本; ViewModel 更新,但文本不变。我是根据有效的示例代码进行编码的。我遵循了它的结构,据我所知,100%准确,但我的不起作用。
如果我将工作演示代码复制粘贴到一个新的空白项目中,它就会工作,所以显然我缺少了一些东西。我也尝试逐行查看它,但没有发现任何可以解释原因的内容。如果我将自己的 mutableStateOf 变量添加到工作代码的视图模型中并在其应用程序中使用它,这也可以。如果我在工作代码中重命名变量、函数名称等,它仍然有效。基本上,在演示代码中执行任何操作都会按其应该/预期的方式工作。但是从头开始创建一个完全遵循其格式的 UI,UI 不会重新组合。
我创建了一个简单的测试应用程序,仅包含
Text
和 Button
。为其创建了一个 ViewModel
扩展类的实例。单击 Button
应该会更改 Text
的内容。相反,什么也没发生。但是 ViewModel
扩展类中的值已更新。没有与该问题相关的错误或警告,并且预览工作正常。
我的代码(不起作用)
TestViewModel.kt:
class TestViewModel: ViewModel(){
var testVar by mutableStateOf("Hi")
fun changeText(){
testVar = "New"
}
}
MainActivity.kt:
class MainActivity: ComponentActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting(viewModel = viewModel())
}
}
}
}
}
@Composable
fun Greeting(viewModel: TestViewModel = viewModel()) {
DisplayOnScreen(testVar = viewModel.testVar, changeText = {viewModel.changeText()})
}
@Composable
fun DisplayOnScreen(testVar: String, changeText: () -> Unit){
Column() {
Text(testVar)
Button(onClick = {changeText}){
Text("Click")
}
}
}
@Preview (showBackground = true, showSystemUi = true)
@Composable
fun GreetingPreview(viewModel: TestViewModel = viewModel()) {
TestAppTheme {
Greeting(viewModel = viewModel())
}
}
演示代码(正在运行)
测试ViewModel.kt:
class TestingViewModel : ViewModel() {
var isCelsius by mutableStateOf(true)
var result by mutableStateOf("")
fun convertTemp(temp: String) {
result = try {
val tempInt = temp.toInt()
if (isCelsius) {
((tempInt * 1.8) + 32).roundToInt().toString()
} else {
((tempInt - 32) * 0.5556).roundToInt().toString()
}
} catch (e: Exception) {
"Invalid Entry"
}
}
fun switchChange() {
isCelsius = !isCelsius
}
}
MainActivity.kt:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JustTheFilesTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
TestTop(viewModel = viewModel())
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NewLines( isCelsius: Boolean, textState: String,
switchChange: () -> Unit, onTextChange: (String) -> Unit
) {
Row (verticalAlignment = Alignment.CenterVertically) {
Switch(checked = isCelsius, onCheckedChange = { switchChange() }
)
OutlinedTextField(value = textState, onValueChange = { onTextChange(it) },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
),
singleLine = true, label = { Text("Enter temperature") },
modifier = Modifier.padding(10.dp),
textStyle = TextStyle(fontWeight = FontWeight.Bold, fontSize = 30.sp),
trailingIcon = {
Icon(
painter = painterResource(R.drawable.baseline_ac_unit_24),
contentDescription = "frost", modifier = Modifier.size(40.dp)
)
}
)
Crossfade(
targetState = isCelsius, animationSpec = tween(2000), label = ""
) { visible ->
when (visible) {
true -> Text("\u2103", style = MaterialTheme.typography.headlineSmall)
false -> Text("\u2109", style = MaterialTheme.typography.headlineSmall)
}
}
}
}
@Composable
fun TestTop(viewModel: DemoViewModel = viewModel()) {
TempConvert(isCelsius = viewModel.isCelsius, result = viewModel.result,
convertTemp = { viewModel.convertTemp(it) },
switchChange = { viewModel.switchChange() }
)
}
@Composable
fun TempConvert( isCelsius: Boolean, result: String,
convertTemp: (String) -> Unit, switchChange: () -> Unit)
{
Column (horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()) {
var textState by remember { mutableStateOf("") }
val onTextChange = { text : String -> textState = text }
Text("Temperature Converter",
modifier = Modifier.padding(20.dp),
style = MaterialTheme.typography.headlineSmall
)
InputRow(
isCelsius = isCelsius, textState = textState,
switchChange = switchChange, onTextChange = onTextChange
)
Text (result, modifier = Modifier.padding(20.dp),
style = MaterialTheme.typography.headlineMedium
)
Button ( onClick = {convertTemp(textState) }
) { Text("Convert Temperature") }
}
}
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun GreetingPreview(model: DemoViewModel = viewModel()) {
JustTheFilesTheme {
ScreenSetup(viewModel = viewModel())
}
}
您需要传递整个 viewModel 而不仅仅是字符串
@Composable
fun DisplayOnScreen(viewModel: TestViewModel, changeText: () -> Unit){
Column() {
Text(viewModel.testVar)
Button(onClick = {changeText}){
Text("Click")
}
}
}
@Composable
fun Greeting(viewModel: TestViewModel = viewModel()) {
DisplayOnScreen(viewModel = viewModel, changeText = {viewModel.changeText()})
}