首页 > 解决方案 > 可变列表更改时视图不会重新组合

问题描述

我有一个在视图模型中有一个状态列表的应用程序。我有在列表中添加数字的功能。当我单击一个按钮时,它会在列表中添加数字。但这些变化并没有反映在另一个视图中。

MainActivity.kt

class MainActivity : ComponentActivity() {

    private val viewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {

                    Column {
                        Button(onClick = {viewModel.increaseCounter()}) {
                            Text("Increase counter")
                        }
                        CountView(viewModel.counter)
                    }
                }
            }
        }
    }
}

@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun CountView(count: List<Int>) {
    var coroutineScope = rememberCoroutineScope()

    coroutineScope.launch {
        Log.d("inside the coroutine ${count}")
    }
}

主视图模型.kt

class MainViewModel: ViewModel() {

    var counter = mutableStateListOf<Int>()

    fun increaseCounter() {
        Log.d(">>>", "in viewmodel ${counter.size}")
        counter.add(1)
    }
}

期望的结果:每当我单击按钮时,必须打印日志,因为它在 mutableStateList 中添加了一个数字。

但是,当我将 mutableStateListOf 更改为 mutableStateOf 并存储一些整数并更改整数时,当我单击按钮时,视图会重新组合并打印日志

标签: androidkotlinkotlin-coroutinesandroid-jetpack-composeandroid-jetpack

解决方案


您的函数未重新组合的主要问题是您没有使用视图本身的值,因此 Compose 缓存它。如果您添加Text(count.joinToString { it.to() })到您的视图中,它将起作用。


但首先你不应该直接从可组合函数中使用协程。并且添加@SuppressLint("CoroutineCreationDuringComposition")不是 IDE 向您显示的错误的正确解决方案:

启动调用应该发生在 aLaunchedEffect而不是组合内

因此,您的代码应如下所示:

@Composable
fun CountView(count: List<Int>) {
    LaunchedEffect(count) {
        println("inside the coroutine $count")
    }
}

这在您的情况下也不起作用,因为 with mutableStateListOf,LaunchedEffect按指针比较键,并且由于这仍然是同一个容器,LaunchedEffect因此不会重新启动。要通过引用进行比较,传递一个简单的列表会更干净:

CountView(viewModel.counter.toList())

请注意,LaunchedEffect您已经在协程范围内并且可以运行挂起函数。

rememberCoroutineScope通常在您需要从其他副作用启动协程时使用,例如按钮单击。

在文档中阅读有关副作用的移动


推荐阅读