(Android Studio) Jetpack Compose、ViewModel 更新,但 UI 未重构

问题描述 投票:0回答:1

我正在尝试在 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())
    }
}
state viewmodel jetpack recompose compose
1个回答
0
投票

您需要传递整个 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()})
}
© www.soinside.com 2019 - 2024. All rights reserved.