为什么在回调中调用 ViewModel 时会发生重组?
Why recomposition happens when call ViewModel in a callback?
我对compose的概念完全一头雾水。
我有一个代码
@Composable
fun HomeScreen(viewModel: HomeViewModel = getViewModel()) {
Scaffold {
val isTimeEnable by viewModel.isTimerEnable.observeAsState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
) {
Switch(
checked = isTimeEnable ?: false,
onCheckedChange = {
viewModel.setTimerEnable(it)
},
)
Clock(viewModel.timeSelected.value!!) {
viewModel.setTime(it)
}
}
}
}
@Composable
fun Clock(date: Long, selectTime: (date: Date) -> Unit) {
NumberClock(Date(date)) {
val time = SimpleDateFormat("HH:mm", Locale.ROOT).format(it)
Timber.d("Selected time: time")
selectTime(it)
}
}
为什么 Clock
小部件在我点击开关时重新组合。如果我从 Clock
中删除行 selectTime(it)
,则不会发生小部件回调重组。
撰写版本:1.0.2
这是因为在 compose 方面,你每次都在创建一个新的 selectTime
lambda,所以重新组合是必要的。如果你传递 setTime
函数作为引用,compose 会知道它是同一个函数,所以不需要重新组合:
Clock(viewModel.timeSelected.value!!, viewModel::setTime)
或者,如果您有更复杂的处理程序,您可以 remember
它。双括号 ({{ }}
) 在这里很重要,因为你需要记住 lambda。
Clock(
date = viewModel.timeSelected.value!!,
selectTime = remember(viewModel) {
{
viewModel.setTimerEnable(it)
}
}
)
我知道它看起来有点奇怪,您可以使用 rememberLambda
这将使您的代码更具可读性:
selectTime = rememberLambda(viewModel) {
viewModel.setTimerEnable(it)
}
请注意,您需要将所有可能更改的值作为键传递,因此 remember
将按需重新计算。
总的来说,重组并不是一件坏事。当然,如果你可以减少它,你应该这样做,但你的代码应该可以正常工作,即使它被重新组合了很多次。例如,您不应在可组合项内进行大量计算来执行此操作,而应使用 side effects.
因此,如果重组 Clock
会导致奇怪的 UI 效果,那么您的 NumberClock
可能有问题,无法在重组后存活下来。如果是这样,请将 NumberClock
代码添加到您的问题中,以获得有关如何改进它的建议。
这是预期的行为。当用户切换开关(通过调用 vm.setTimeenabled)时,您显然正在修改视图模型中的 isTimeEnabled
字段。现在,您的视图模型中的 isTimeEnabled
显然是一个 LiveData
实例,并且您通过调用 observeAsState
从 Composable 中引用该实例。因此,当您修改开关的 onValueChange 的值时,您实际上是在修改可组合项所依赖的状态。因此,为了呈现更新后的状态,会触发重组
我对compose的概念完全一头雾水。 我有一个代码
@Composable
fun HomeScreen(viewModel: HomeViewModel = getViewModel()) {
Scaffold {
val isTimeEnable by viewModel.isTimerEnable.observeAsState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
) {
Switch(
checked = isTimeEnable ?: false,
onCheckedChange = {
viewModel.setTimerEnable(it)
},
)
Clock(viewModel.timeSelected.value!!) {
viewModel.setTime(it)
}
}
}
}
@Composable
fun Clock(date: Long, selectTime: (date: Date) -> Unit) {
NumberClock(Date(date)) {
val time = SimpleDateFormat("HH:mm", Locale.ROOT).format(it)
Timber.d("Selected time: time")
selectTime(it)
}
}
为什么 Clock
小部件在我点击开关时重新组合。如果我从 Clock
中删除行 selectTime(it)
,则不会发生小部件回调重组。
撰写版本:1.0.2
这是因为在 compose 方面,你每次都在创建一个新的 selectTime
lambda,所以重新组合是必要的。如果你传递 setTime
函数作为引用,compose 会知道它是同一个函数,所以不需要重新组合:
Clock(viewModel.timeSelected.value!!, viewModel::setTime)
或者,如果您有更复杂的处理程序,您可以 remember
它。双括号 ({{ }}
) 在这里很重要,因为你需要记住 lambda。
Clock(
date = viewModel.timeSelected.value!!,
selectTime = remember(viewModel) {
{
viewModel.setTimerEnable(it)
}
}
)
我知道它看起来有点奇怪,您可以使用 rememberLambda
这将使您的代码更具可读性:
selectTime = rememberLambda(viewModel) {
viewModel.setTimerEnable(it)
}
请注意,您需要将所有可能更改的值作为键传递,因此 remember
将按需重新计算。
总的来说,重组并不是一件坏事。当然,如果你可以减少它,你应该这样做,但你的代码应该可以正常工作,即使它被重新组合了很多次。例如,您不应在可组合项内进行大量计算来执行此操作,而应使用 side effects.
因此,如果重组 Clock
会导致奇怪的 UI 效果,那么您的 NumberClock
可能有问题,无法在重组后存活下来。如果是这样,请将 NumberClock
代码添加到您的问题中,以获得有关如何改进它的建议。
这是预期的行为。当用户切换开关(通过调用 vm.setTimeenabled)时,您显然正在修改视图模型中的 isTimeEnabled
字段。现在,您的视图模型中的 isTimeEnabled
显然是一个 LiveData
实例,并且您通过调用 observeAsState
从 Composable 中引用该实例。因此,当您修改开关的 onValueChange 的值时,您实际上是在修改可组合项所依赖的状态。因此,为了呈现更新后的状态,会触发重组