kotlin (Android Studio)Jetpack Compose,ViewModel更新,但UI未重新组合

7cwmlq89  于 2023-10-23  发布在  Kotlin
关注(0)|答案(2)|浏览(168)

我试图在Jetpack Compose中测试ViewModel概念,我被这个问题难倒了,它让我花了一整天的时间无法弄清楚。在调试应用程序时,我看到ViewModel实际上正在更新,但它不会导致我的UI在更新后重新组合。
作为这个概念的一个更小的测试,我创建了一个应用程序,实际上只有一个文本和按钮元素组合。点击这个按钮是为了在更新ViewModel后改变文本; ViewModel会更新,但文本不会更改。我是基于示例代码进行编码的。我按照它的结构,100%准确,据我所知,但我的不工作。
如果我将工作演示代码复制粘贴到一个新的空白项目中,它就可以工作,所以很明显我遗漏了一些东西。我也试着一行一行地看它,没有发现任何可以解释原因的东西。如果我将自己的mutableStateOf变量添加到工作代码的视图模型中,并在其应用程序中使用它,也可以工作。如果我在工作代码中重命名变量,函数名等,它仍然有效。基本上,在演示代码中做任何事情都应该/预期地工作。但是从头开始创建一个完全遵循其格式的UI,它不会重新组合。
我用TextButton创建了一个简单的测试应用程序。为它创建了一个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())
        }
}

演示代码(工作)

TestingViewModel.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: TestingViewModel = 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: TestingViewModel = viewModel()) {
    JustTheFilesTheme {
        ScreenSetup(viewModel = viewModel())
    }
}
bwleehnv

bwleehnv1#

您应该remember该值,以确保Compose框架成功跟踪更改。例如,在Greeting组合中,确保将状态值 Package 在remember中(它还支持委托by)。有了这个,Compose就知道该怎么做了。

@Composable
fun Greeting(viewModel: TestViewModel = viewModel()) {
  val rememberedtestVar by remember { viewmodel.testVar } 
  DisplayOnScreen(testVar = rememberedtestVar, changeText 
 = {viewModel.changeText()})
}

编辑:这似乎与Jetpack Compose本身无关,但与您修改文本的方式有关。在代码中,你似乎传递了一个lambda,但当你点击按钮时,你没有调用它,你只是调用lambda而没有调用,这就是我所说的:

@Composable
fun DisplayOnScreen(testVar: String, changeText: () -> Unit){
        Column() {
                Text(testVar)
                Button(onClick = {changeText}){
                        Text("Click")
                }
        }
}

应该是这样的吧?:

@Composable
fun DisplayOnScreen(testVar: String, changeText: () -> Unit){
        Column() {
                Text(testVar)
                Button(onClick = { changeText.invoke() }){
                        Text("Click")
                }
        }
}
hujrc8aj

hujrc8aj2#

你需要传递整个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()})
}

相关问题