有这样的代码:
@Composable
fun FragmentA(navController: NavController) {
val vm = viewModel(VM::class.java)
val a by vm.a.observeAsState("")
val b by vm.b.observeAsState("")
Column {
MyRandomBox()
MyBox(a)
MyBox2(b)
}
}
@Composable
fun MyRandomBox() {
Text(text = UUID.randomUUID().toString())
}
@Composable
fun MyBox(text: String) {
Box {
Log.e("!!!", "box-1")
Text(text = text)
}
}
@Composable
fun MyBox2(text: String) {
Box {
Log.e("!!!", "box-2")
Text(text = text)
}
}
class VM : ViewModel() {
val a = MutableLiveData<String>()
val b = MutableLiveData<String>()
init {
viewModelScope.launch {
(0..100).forEach {
a.value = "a-$it"
delay(1000)
b.value = "b-$it"
delay(1000)
}
}
}
}
它工作得很好:每秒 1 个 LiveDats 发生变化,并且只有发生变化的 Box 才会显示在日志中。
第一个文本中的值,因为它是在启动时随机创建的,所以仍然挂起(它不会再次随机化)。
但是如果你稍微改变一下代码,就像这样:
Column {
// MyRandomBox()
Text(text = UUID.randomUUID().toString())
MyBox(a)
MyBox2(b)
}
那么现在当您更改任何 LiveDates 时,第一个文本中的文本将再次随机化
为什么如果您将这段代码封装在自己的代码中,Composable-функцию
一切都会按其应有的方式工作,但如果没有它,一切都不会?(就好像视图是从头开始重新创建的)
UPD
以下行为也不清楚:
取消了操作onClick
@Composable
fun FragmentA(navController: NavController) {
val vm = viewModel(VM::class.java)
val a by vm.a.observeAsState("")
val b by vm.b.observeAsState("")
Column {
MyRandomBox()
MyBox(a) {
vm.rnd()
}
MyBox2(b)
}
}
@Composable
fun MyBox(text: String, onClick: () -> Unit) {
Box {
Log.e("!!!", "box-1")
Text(text = text,
modifier = Modifier
.clickable {
onClick()
}
)
}
}
LOG
10:04:12.770 E box-1
10:04:13.771 E box-1
10:04:13.772 E box-2
10:04:14.786 E box-1
10:04:15.787 E box-1
10:04:15.790 E box-2
10:04:16.786 E box-1
10:04:17.786 E box-1
10:04:17.787 E box-2
当它改变时,a
它会被记录box-1
当b
它改变时,它会被记录box-1
+box-2
但如果你用它替换它vm.rnd()
,Log.e()
一切都会按预期工作。
为什么会发生这种情况?
读取状态
a
and you 在调用函数和时b
在内部发生。因为 - 内联函数,然后重建整个函数,并在同一片段中调用获取 UUID 的函数。Column
MyBox
MyBox2
Column
FragmentA
如果选择了单独的函数
MyRandomBox
,那么重组FragmentA
不会影响它,因为 此函数(具有接收 UUID 的自己的作用域)在重组期间没有更改的状态FragmentA
。如果将状态的读取重写为函数
MyBox
,那么就不会出现片段的重组,只会出现重组MyBox
。更多详细信息(developer.android.com)
举个例子,要了解函数的作用域在哪里,如果
MyBox
您根本不将其分离为单独的函数并在某些非内联函数的作用域内调用读取,它也会起作用,例如Surface
:PS 由于这种含糊不清,我不喜欢流行的by function 。我更喜欢直接声明
State
并在读取时立即看到状态值。关于
onClick
:ViewModel 不被视为稳定类,并且调用不稳定类(未包装在 )中的 lambda
remember { }
可以在重构创建它的作用域时重新创建(当和更改FragmentA
时,您仍然可以重构它)。a
b
如果像这样单独声明一个 lambda:
val onClick = remember { { vm.rnd() } }
然后像这样使用它:MyBox(a, onClick)
,那么更改b
将不再影响MyBox
。另外,您需要更改
Modifier.clickable { onClick() }
为Modifier.clickable(onClick = onClick)
. 原始选项意味着您创建一个新的 lambda 来调用之前创建的 lambdaonClick
。在新版本中,之前创建的 lambda 被简单地转移到修饰符中。