更改列表项类中的属性值时,Jetpack compose mutableStateOf 列表不会触发重新组合

Tic*_*ers 5 android kotlin data-class android-jetpack-compose compose-recomposition

我认为我在这里缺少 Jetpack Compose 的核心概念。non-constructor data class property当我尝试更改可组合项的内部且此可组合项是观察列表的一部分时,我遇到了问题。

不起作用:(sadProperty未在构造函数中声明)

data class IntWrapper(val actualInt: Int = 0) {
var sadProperty: Int = 0
}

@Preview
@Composable
fun test() {
var state by remember { mutableStateOf(listOf(IntWrapper(1), IntWrapper(2), IntWrapper(3),IntWrapper(4)))}

    fun onClick(item: IntWrapper) {
        val indexOf = state.indexOf(item)
        val newState = state.minus(item).toMutableList()
        val copy = item.copy()
        copy.sadProperty = Random.nextInt()
        newState.add(indexOf, copy)
        state = newState
    }
    
    Column() {
        for (item in state) {
            Text("ac: ${item.actualInt} sad: ${item.sadProperty}", modifier = Modifier.clickable { onClick(item)})
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

作品:(actualInt在构造函数中声明)

data class IntWrapper(var actualInt: Int = 0) {
var sadProperty: Int = 0
}

@Preview
@Composable
fun test() {
var state by remember { mutableStateOf(listOf(IntWrapper(1), IntWrapper(2), IntWrapper(3),IntWrapper(4)))}

    fun onClick(item: IntWrapper) {
        val indexOf = state.indexOf(item)
        val newState = state.minus(item).toMutableList()
        val copy = item.copy()
        copy.actualInt = Random.nextInt()
        newState.add(indexOf, copy)
        state = newState
    }
    
    Column() {
        for (item in state) {
            Text("ac: ${item.actualInt} sad: ${item.sadProperty}", modifier = Modifier.clickable { onClick(item)})
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么会发生这种情况吗?

z.g*_*g.y 4

这看起来像是一个Jetpack Compose关于Kotlin数据类的问题,对我来说,我会尽力而为。

让我们首先从 Kotlin 的 Data 类开始

根据kotlin 文档关于Data Class

编译器自动从主构造函数中声明的所有属性派生以下成员:

  • equals()/hashCode() 对
  • toString() 形式为“User(name=John,age=42)”
  • componentN() 函数对应于属性的声明顺序。
  • 复制() 。

您的IntWrapper数据类有一个Primary Constructor、类名后面的括号,以及在其中声明的 1 个属性。

data class IntWrapper(val actualInt: Int = 0) {
      var sadProperty: Int = 0
}
Run Code Online (Sandbox Code Playgroud)

这样,我们可以说,你的IntWrapper数据类有

  • 1 个组件 ( actualInt)
  • 表单的 toString()IntWrapper(actualInt=?)
  • 生成copy()函数
  • 生成的 equals()/hashCode() 对

并再次基于文档

编译器仅使用主构造函数中定义的属性来自动生成函数。要从生成的实现中排除某个属性,请在类主体中声明它:

只会使用/评估从主构造函数(即)equals声明的属性,并且由于它位于数据类主体的一部分而被排除在外。IntWrapper'sactualInt : IntsadProperty

现在考虑以下事项:

val intWrapper1 = IntWrapper(actualInt = 5)
intWrapper1.sadProperty = 5

val intWrapper2 = IntWrapper(actualInt = 5)
intWrapper2.sadProperty = 10

Log.e("AreTheyEqual?", "${intWrapper1 == intWrapper2}")
Run Code Online (Sandbox Code Playgroud)

它打印,

E/AreTheyEqual?: true
Run Code Online (Sandbox Code Playgroud)

因为equality看到两个派生属性具有相同的值5sadProperty所以被排除在此比较之外。

val intWrapper1 = IntWrapper(actualInt = 5)
intWrapper1.sadProperty = 5

val intWrapper2 = IntWrapper(actualInt = 10)
intWrapper2.sadProperty = 5
Run Code Online (Sandbox Code Playgroud)

印刷,

E/AreTheyEqual?: false
Run Code Online (Sandbox Code Playgroud)

因为生成的 equals 验证了两个实例中生成的组件 ( actualInt) 不相同IntWrapper

现在,将Jetpack Compose我们所理解的一切应用到数据类中,

  • 第一个test符合有关的所有内容data class,它创建一个具有新值的新对象,这就是Compose需要触发的内容re-composition

  • 第二个test不会触发re-compositionCompose仍然会看到相同的IntWrapper实例,因为sadProperty它不是数据类的 equals 操作将使用的生成组件的一部分。