Jetpack Compose - 再谈mutableStateOf(二)

4,676 阅读1分钟

我的list 为啥不更新?

先看下面的代码:

var data by mutableStateOf(mutableListOf(1, 2, 3))
setContent {

    Column {
        Button(onClick = { data.add(data.last() + 1) }) {
            Text(text = "onclick")
        }
        for (d in data){
            Text(text = "data:${d}")
        }
    }
}

执行以后 你会发现 虽然点击以后 data的值应该是新增了,但是界面却没有发生任何变化

我们稍微修改一下:

var data by mutableStateOf(mutableListOf(1, 2, 3))
var data2 by mutableStateOf(0)
setContent {

    Column {
        Text(text = "$data2", modifier = Modifier.clickable { data2++ })
        Button(onClick = { data.add(data.last() + 1) }) {
            Text(text = "onclick")
        }
        for (d in data) {
            Text(text = "data:${d}")
        }
    }
}

相比于第一段代码,他新增了一个text组件,这个时候 如果我们这么操作:

  1. 先点击button
  2. 发现 列表组件没有更新 3.再点击一下text,这个时候data2的值发生了变化,Text自然而然也变了 4.紧接着 下面的列表的ui 也发生了变化,刷新到了最新的 list的状态,

这是为啥?

前面一篇文章介绍过:

image.png

只有set方法才会触发监听,你看看之前的 data.add 这个操作 是set方法吗? 显然不是!讲白了

所谓setValue的操作 就是 变更你索引的指向,类似于:

var studtent=Student("xiaomi")

然后我们

studutent=Stutent("huawei")

这个才叫 setvalue

类似于这种

student.name="huawei“

这种不叫setvalue了,这种叫 对一个对象内部的某个值 做了改变。

这就是为啥第一段代码 ui没有发生变化的根本原因

那第二段为啥可以更新了?

因为 你data2的值 更新了吧,他触发了与他绑定的Text的更新, 但是这个data2的Text与我们 list里面的Text是同一级的,同属于同一个作用域,所以他更新了,我这里面的list也要跟着更新,这个被动更新 最终触发了list ui的变化

list 不更新的解决方案

现在来看看 刚才的问题怎么修改,其实很简单,我们只要触发一下setValue 不就行了吗

Button(onClick = {

    data = data.toMutableList().apply {
        add(data.last() + 1)
    }
}) 

返回一个新的mutableList,触发了setValue 列表自然就会自动更新了。

有更加好的方案吗?有的

// 注意这里的写法 
val data = mutableStateListOf(1, 2, 3)
setContent {

    Column {
        Button(onClick = {
            data.add(data.last() + 1)
        }) {
            Text(text = "onclick")
        }
        for (d in data) {
            Text(text = "data:${d}")
        }
    }
}

首先这个写法上 看上去比之前的写法要干净利落了不少吧,点击事件是一个 利落的list 的add操作,而且list ui 也可以 正确刷新界面,主要就是注意

mutableStateListOf 这个东西

他是谷歌专门提供出来的,用于监听list内部变化的 辅助类,你的list内部有了变化,他也可以正确及时的反应在界面上。 有人可能要问了,为啥 这里没用by 关键字了?

因为前面一篇文章也讲过,by关键字 其实就是给 一组 setValue和getValue的扩展函数,而这里我们的目的是要监听 list内部元素的变化,那自然是用不到by的, 所以索性 我们的var也可以用val直接替换。

与之对应的 还有 mutableStateMapOf

mutableStateListOf 这个也不更新了?

来看下面的代码

data class Student(var name: String = "", var age: Int = 0)

val data = mutableStateListOf(Student("xiaomi", 1), Student("huawei", 2), Student("oppo", 3))
setContent {
    Column {
        Button(onClick = {
            data[2].age = 100
        }) {
            Text(text = "onclick")
        }
        for (d in data) {
            Text(text = "data:${d}")
        }
    }
}

一开始有个list ,我们点击以后想让第三个元素的age 展示为100 并且我们也使用了mutableStateListOf

但是界面依然没有变化,为啥?

其实原因还是和前面一样的,你没有对setValue进行操作,你操作的还是Student这个list中 某一个元素的 某一个fieid的值而已啊

所以这里应该怎么改?

我们触发一下这个setValue操作就可以了

val data = mutableStateListOf(Student("xiaomi", 1), Student("huawei", 2), Student("oppo", 3))
setContent {
    Column {
        Button(onClick = {
            // 这里只要触发赋值操作即可
            data[2] = data[2].copy(age = 100)
        }) {
            Text(text = "onclick")
        }
        for (d in data) {
            Text(text = "data:${d}")
        }
    }
}

还有没有其他改法?有的

class Student(var name: String = "") {
    //主要修改点在这里
    var age by mutableStateOf(0)
}
val data = mutableStateListOf(Student("xiaomi"), Student("huawei"), Student("oppo"))
setContent {
    Column {
        Button(onClick = {
            // 这里只要触发赋值操作即可
            data[2].age = 100
        }) {
            Text(text = "onclick")
        }
        for (d in data) {
            Text(text = "data:${d.name} ${d.age}")
        }
    }
}

我们只要把age也设置成mutableStateOf 即可。原因就不说了哈,前面强调过多次了。