在 Android Compose 中重新组合

Mr.*_*hie 4 android android-jetpack android-jetpack-compose android-jetpack-compose-text

我正在使用新的撰写库在 android 中实现OutstandingTextField 。但奇怪的是,文本字段中的输入数据没有更新。

于是我搜索了一下,发现了一个叫做Recomposition in android compose的主题。我没有完全明白。
不过,我确实找到了解决方案:

@Composable
fun HelloContent(){
    var name:String by remember {mutableStateOf("")}
    OutlinedTextField(
        value = name,
        onValueChange = {name = it},
        label = {Text("Name")}
    )
}
Run Code Online (Sandbox Code Playgroud)

我还阅读了jetpack compose 中的状态概念。但我没能完全理解它。
有人可以用简单的话解释一下吗?

Ric*_*per 20

基本上,重组只是 Compose 中的一个事件,其中相关的可组合项被重新执行。在声明性编码(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)实际上会返回999type Int,而不是 type MutableState<Int>,但最精彩的部分是 Compose 仍然知道该变量a是 State-Holder。所以基本上mutableStateOf可以被认为是一个注册计数器,你只需要通过它一次,就可以在 的列表中注册State-Holders。从那时起,每当其中一个国家持有者的价值发生变化时,就会触发重组。这是粗略的想法,但让我们来谈谈技术吧;现在讨论“如何”重组。

要触发重组,您需要确保两件事:

  1. 可组合项应该读取一个变量,它也是一个状态持有者
  2. 国家持有者的当前价值应该发生变化

有了佩里,一切都会变得更好示例:-

var a by mutableStateOf(999)

情况 1:可组合项接收a参数值 ,MyComposable(a)然后运行a = 0​​,结果 1:触发重组

情况二:这个变量声明a实际上是在可组合本身内部,然后我运行a = 12344 结果二:触发重组

案例 III:我重复案例 1 和 II,但使用不同的变量,如下所示:var b = 999
结果 III:未触发重组;原因:b不是国家持有者

太好了,我们现在已经掌握了基础知识。那么,这是本次讲座的最后一个阶段。

记住!!

你看,当我说在重组期间,整个 Composable 被重新执行,我的意思是整个 Composable 被重新执行,也就是说,每一行和每一个赋值都被重新执行,没有例外。你发现这有什么问题了吗?让我演示一下

假设我想要一个Text可组合项显示一个数字,并在单击它时增加该数字。

我可以实现像这样简单的东西

@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++ }
 )
}
Run Code Online (Sandbox Code Playgroud)

好的,这就是我们认为可行的实现。如果您不熟悉Modifiers,请暂时保留它并相信我,clickable当您实际单击 时,它只会触发大括号内的代码Text。现在,让我们想象一下这将如何执行。

首先,Compose 会将变量注册n为状态持有者。然后它将渲染Text初始值为0的Composable n

现在,它Text实际上是click编辑过的。里面的块clicakble将被执行,在本例中只是n++,这将更新 的值n。Compose 看到 的值n已更新,并遍历状态持有者列表。Compose 发现n确实是状态持有者,然后决定触发重组。现在,整个 Composable 的读取值n将被重新组合。在这种情况下,该可组合项是 CountingText因为Text它的内部正在读取(以显示它)的值n。因此,CountingText将被“重新执行”。让我们来看看这里的重新执行。

可组合项中的第一行,

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
 )
Run Code Online (Sandbox Code Playgroud)

所以你看,这里的问题是,在重新执行时,它n是完全从头开始创建的,就好像它以前从未存在过一样。它已从可组合项的内存中删除。为了解决这个问题,我们需要 Composable to remember n. 这样,Compose 就知道这是一个状态持有者,并且拥有一个需要在重组时重新分配给它的值。所以,这是更新后的第一行(其余部分相同,只是更新了初始化)

var n by remember { mutableStateOf(0) }

现在,在第一次执行时,n将收到 0,因为它实际上是第一次n创建。感谢remembern现在可以访问 的Composable内存,因此将存储在内存中以供将来使用。

因此,在重组过程中,会发生这样的情况 - 当执行器 (???) 到达分配的行时n

var n by remember { mutableStateOf(0) }

remember实际上充当看门人的角色,不允许执行者进入其中包含的块。相反,它会将之前记住的值传递给它并要求它继续前进。由于当用户单击 时Text,它已经将 的值增加到n1,该值保留在内存中,因此现在可以按预期工作。

这与您的问题的情况相同TextField。该字段最初读取一个空值,每次用户键入字母时该值都会更新,触发重组并最终在屏幕上显示正确的值。

它能变得足够简单吗?让我知道我花了半个小时打字。