在 Android 中重组

Recompose in Android Compose

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

所以我搜索了一下,在android compose中找到了一个叫做Recomposition的话题。没看懂。
但是,我确实找到了解决方案:

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

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

Recomposition 在 Compose 中用于重新组合(重新加载更改的部分)屏幕。在您的示例中,您有更改屏幕状态的用户输入。您必须使用 var name:String by remember {mutableStateOf("")},顺便说一句,您可以将 :String 排除在外,因为您在此处设置了类型:mutableStateOf("") 无论如何,您需要使用 remember 可组合项记住旧内容,然后可以添加新内容。 如果每次输入一个字母时输入 h e l l o,它都会重新组合,它会记住值 h,然后是 he,然后是 hel,依此类推。

试试这个
我知道没有区别但留下进口

Compose_version 1.0.4

import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember

@Composable
fun YourAnswer() {
    var text by remember { mutableStateOf("") }
    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Name") }
    )
}

基本上,重组只是Compose中的一个事件,在这个事件中Composable被重新执行。在 Compose 所基于的声明式编码中,我们将 UI 编写为函数(或更常见的方法)。现在,重组基本上是一个事件,其中 UI 通过重新执行可组合“函数”的主体 重新发出 。这就是重组的核心。现在谈谈它何时被触发。

好的,所以为了触发重组,我们需要一种特殊类型的变量。这种类型内置于 compose 中,专门设计用于让它知道何时进行重组。而提到的类型是MutableState。顾名思义,就是State,可以Mutate,即改变;变化。

所以,我们有一个MutableState类型的变量,下一步是什么?你猜怎么着,你没有 MutableState 类型的变量,因为我没有教你如何创建一个!您将在 Compose 中使用的最常见的赋值是 mutableStateOf。这是一个预定义的方法,return 是一个 MutableState 类型的值,嗯,实际上是 MutableState<T>T这里是State的类型,见下文

var a = mutableStateOf(999)

上面,你可以看到,999是一个int,所以这里的mutableStateOf会return一个MutableState<Int>类型的值。很简单。

现在,我们有一个 MutableState<Int> 值,但老实说,这有点难看。每次需要从 MutableState<T> 中获取值时,都需要引用 属性,方便地命名为 .value

因此,要从上述 var a 中得到 999,您需要调用 a.value。现在,这在一个或两个地方使用很好,但每次调用它似乎一团糟。这就是 Kotlin Property Delegation 进来的地方(我不需要将最后两个词大写,我知道)。我们使用 by 关键字从状态中检索值,并将其分配给我们的变量——这就是您应该关心的。

因此,var a by mutableStateOf(999) 实际上会 return 999 类型 Int,而不是 MutableState<Int> 类型,但出色的部分是 Compose 将仍然知道变量 a 是一个 State-Holder。所以基本上 mutableStateOf 可以被认为是一个注册计数器,你只需要通过一次,就可以在 State-Holders 的列表中注册。从那时起,每当其中一个国家持有者的价值发生变化时,都会触发重组。这是粗略的想法,但让我们了解技术。现在,我们可以开始了解重组的“方式”了。

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

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

有了 Perry 一切都变得更好了示例:-

var a by mutableStateOf(999)

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

案例二:变量 a 实际上是在 Composable 本身内部声明的,然后我 运行 var a = 12344 结果二:触发重组

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

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

记住!!

你看我说during recomposition,re-executive the entire Composable,我的意思是整个Composable都重新执行,也就是每一行,每一个assignment,无一例外。你看出这有什么问题了吗?让我演示一下

假设我想要一个 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++ }
 )
}

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

首先,Compose 会将变量 n 注册为状态持有者。然后它将使用 n.

的初始值 0 渲染 Text Composable

现在,Text 实际上是 clicked。 clicakble 中的块将被执行,在本例中就是 n++,它将更新 n 的值。 Compose 看到 n 的值已更新,并且 运行 遍历状态持有者列表。 Compose 发现 n 确实是一个状态持有者,然后决定触发重组。现在,读取 n 值的整个 Composable 将被重组。在这种情况下,可组合项是 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
 )

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

var n by remember { mutableStateOf(0) }

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

因此,在重组期间,会发生以下情况 - 当执行程序 (???) 到达分配 n 的行时,

var n by remember { mutableStateOf(0) }

remember实际上起到看门人的作用,不允许执行者进入其中包含的区块。相反,它将先前记住的值传递给它并要求它继续前进。因为当用户单击文本时,它已经将 n 的值递增到 1,它保留在内存中,因此,现在它按预期工作。

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

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