基本上,重组只是 Compose 中的一个事件,其中 Composable 被重新执行。在 Compose 所基于的声明式编码中,我们将 UI 编写为函数(或方法,更常见)。现在,重组基本上是通过重新执行可组合“函数”的主体来重新发出UI 的事件。这就是重组的核心。现在到它何时被触发。
好的,所以为了触发重组,我们需要一种特殊类型的变量。这种类型内置于 compose 中,专门设计用于让它知道何时重新组合。并且提到的类型是MutableState
. 顾名思义,就是State,可以Mutate,即改变;各不相同。
所以,我们有一个类型的变量MutableState
,接下来是什么?你猜怎么着,你没有类型变量,MutableState
因为我没有教你如何创建一个!您将在 Compose 中使用的最常见分配是mutableStateOf
. 这是一个预定义的方法,它实际上返回一个类型的MutableState
值MutableState<T>
。T
是这里的State类型,见下文
var a = mutableStateOf(999)
如上所示,999 是一个 int,因此,mutableStateOf
这里将返回一个MutableState<Int>
类型值。很容易。
现在,我们有一个MutableState<Int>
值,但老实说,这有点难看。每次需要从 中获取值时MutableState<T>
,都需要引用一个方便命名的属性.value
。
因此,要从上述 999 中取出var a
,您需要调用a.value
. 现在,这可以在一两个地方使用,但每次调用它似乎都是一团糟。这就是 Kotlin 属性委托的用武之地(我知道,我不需要将最后两个词大写)。我们使用by
关键字从状态中检索值,并将其分配给我们的变量 - 这就是您应该关心的全部。
所以,var a by mutableStateOf(999)
实际上会返回999
type Int
,而不是 type MutableState<Int>
,但精彩的部分是 Compose 仍然会知道该变量a
是 State-Holder。所以基本上mutableStateOf
可以被认为是一个注册计数器,你只需要通过一次,就可以在State-Holders
. 从何时开始,每次更改状态持有者之一的值时都会触发重组。这是一个粗略的想法,但让我们了解一下技术。现在,我们可以了解重组的“方式”。
要触发重组,您只需要确保两件事:
- Composable 应该读取一个变量,这也是一个状态持有者
- 状态持有者应该经历其当前价值的变化
佩里的例子让一切都变得更好:-
var a by mutableStateOf(999)
案例 1:一个 Composable 接收a
作为参数值MyComposable(a)
,然后我运行a = 0
,结果 1:Recomposition Triggered
案例二:变量a
实际上是在 Composable 内部声明的,然后我运行var a = 12344
结果二:触发重组
案例 III:我重复案例 1 和 II,但使用不同的变量,如下所示:var b = 999
结果 III:未触发重组;原因:b
不是国家持有者
太好了,我们现在掌握了基础知识。所以,这是本次讲座的最后阶段。
记住!!
你看,当我说在重组期间,整个 Composable 被重新执行,我的意思是整个 Composable 被重新执行,也就是说,每一行和每一个分配,没有例外。你看到这有什么问题了吗?让我示范
假设我想要一个Text
Composable 显示一个数字,并在我单击它时增加该数字。
我可以实现这样简单的东西
@Composable
fun CountingText(){
var n = mutableStateOf(0) //Starts at 0
Text(
value = n.toString(), //The Composable only accepts strings, while n is of Int type
modifier = Modifier
.clickable { n++ }
)
}
好的,这是我们可能认为可行的实现。如果您不熟悉Modifier
s,请暂时保留它,并相信我clickable
,当您实际单击Text
. 现在,让我们想象一下这将如何执行。
首先 Compose 会将变量注册n
为状态持有者。然后它将呈现Text
具有初始值0
的Composable n
。
现在,Text
实际上是click
ed。里面的块clicakble
将被执行,在这种情况下只是n++
,它将更新 的值n
。Compose 看到 的值为n
更新,并遍历状态持有者列表。Compose 发现n
确实是一个状态持有者,然后决定触发重组。现在,读取 的值的整个 Composablen
将被重新组合。在这种情况下,该 Composable 是 CountingText
因为它Text
内部正在读取n
(To display it) 的值。因此,CountingText
将被“重新执行”。让我们来看看这里的重新执行。
Composable 中的第一行,
var n = mutableStateOf(0)
n
变成了0。
下一行:-
Text(
value = n.toString(), //Just displays 0
modifier = Modifier
.clickable { n++ } //Just tells it to increase n upon click
)
所以你看,这里的问题是,在重新执行时,n
它是完全从头开始创建的,就好像它以前从未存在过一样。它已从 Composable 的内存中删除。为了解决这个问题,我们需要 Composable to remember
n
. 这样,Compose 就知道这是一个状态持有者,并且拥有一个需要在重组时重新分配给它的值。所以,这里只是更新的第一行
var n by remember { mutableStateOf(0) }
现在,在第一次执行时,n
将收到 0,因为它实际上是第一次n
创建。感谢remember
,n
现在可以访问Composable
的内存,因此将存储在内存中以供将来使用。
因此,在重组期间,会发生这种情况 - 当执行程序 (???) 到达n
分配的行时,
var n by remember { mutableStateOf(0) }
remember
实际上充当了看门人,并且不允许执行者进入其中包含的块。相反,它将先前记住的值传递给它并要求它继续前进。因为当用户单击文本时,它已经将值n
增加到 1,这被保留在内存中,所以现在它可以按预期工作。
这与您的 TextField 问题相同。该字段最初读取一个空值,并且每次用户键入一个字母时都会更新该值,触发重组并最终在屏幕上显示正确的值。
它可以变得足够简单吗?让我知道我花了半个小时打字。