聊聊 Jetpack Compose 的 “状态订阅自动刷新” -- mutableStateListOf

news/2025/2/14 0:23:15/

Jekpack Compose “状态订阅&自动刷新” 系列:

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - MutableState/mutableStateOf 】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - remember 和重组作用域 】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - 有状态、无状态、状态提升?】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - mutableStateListOf 】

     【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - 你真的了解重组吗?】


讲任何一个新的主题或者知识点,习惯性的从 Demo 开始,比如:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var name by mutableStateOf("Hi, Compose")setContent {Text(name)}}
}

这段代码已经熟的不能再熟了,如果我们用 by mutableStateOf 初始化一个变量,那 name 就会变成一个被 Compose 自动订阅的变量。

我们前面所有的例子,都是用 by mutableStateOf 包了一个 String,如果换成别的类型,行不行?肯定可以,不用想。

fun <T> mutableStateOf(value: T,  // 泛型参数policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)

比如 Int 类型:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var num by mutableStateOf(1)setContent {Text(text = "当前数值:$num",Modifier.clickable {num++})}}
}

代码不做解释了,直接看效果:

在这里插入图片描述

比如:List 类型

// num 类型:MutableList<Int>
// mutableStateOf 类型:MutableState<MutableList<Int>>
var nums by mutableStateOf(mutableListOf(1, 2, 3))

我们在代码里面用起来:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var nums by mutableStateOf(mutableListOf(1, 2, 3))setContent {Column {for (num in nums) {Text("第 $num 块文字")}}}}
}

运行:

在这里插入图片描述

现在我们稍微改下代码:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var nums by mutableStateOf(mutableListOf(1, 2, 3))setContent {Column {Button(onClick = {nums.add(nums.last() + 1)}) {Text("List 加 1")}for (num in nums) {Text("第 $num 块文字")}}}}
}

代码很简单:我们添加了一个 Button,每次点击后,nums 会添加一个值,比最有一个值大 1。

运行:

在这里插入图片描述

???没生效啊!为什么?

我们先来考虑一个问题,mutableStateOf 原理是什么?前面的问题我们说过,可以回忆一下:

mutableStateOf 之所以可以对变量进行订阅和刷新,主要是因为内部的 get() 和 set() 方法加了钩子,或者说它的 set() 方法是赋值!是改变了变量的指向,它是直接把对象可替换了,但在我们这个代码里面 nums 仅仅是改变了它内部的状态:

在这里插入图片描述

对这块如果有点懵,可以看【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - MutableState/mutableStateOf 】 这篇文章。

所以,它不会出发 setValue() 的调用,所以不会出发自动刷新的操作。

在这里插入图片描述

为了验证是不是因为没有重组,我们可以验证下:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var nums by mutableStateOf(mutableListOf(1, 2, 3))var refresh by mutableStateOf("强制刷新")setContent {Column {Text(refresh, Modifier.clickable { refresh = "刷新完成"})Button(onClick = {nums.add(nums.last() + 1)}) {Text("List 加 1")}for (num in nums) {Text("第 $num 块文字")}}}}
}

我们添加了一个 Text() 组件,改变 refresh 的值,那么理论上它就会带着整个重组作用域内的组件全部刷新,包括 List。

运行:

在这里插入图片描述

成功刷新 List!

现在我们就很清楚了,mutableStateOf 没法对 List 类型的对象没法实现类似 String、Int 的自动订阅及刷新,那有没有解决办法?

上面我们说过了,问题的根本原因是 List 只是内部的变化,而不是它自己本身对象的变化,那我们直接在内部操作完后直接把 List 重新给换了不就行了,试试:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var nums by mutableStateOf(mutableListOf(1, 2, 3))setContent {Column {Button(onClick = {// nums 重新赋值nums = nums.toMutableList().apply {add(nums.last() + 1)}}) {Text("List 加 1")}for (num in nums) {Text("第 $num 块文字")}}}}
}

运行:

在这里插入图片描述

但这样写就会显得很奇怪,我是明白原理了,我这样也是可以实现正常的刷新,但不太对劲:既然对于 String、Int 这些类型,Compose 提供了 mutableStateOf,难道对于 List 这种这么常用的类型,就没有一个 mutable*** 的函数给我们用?

有,它就是 mutableStateListOf!它可以观测到内部 List 的数据变化!

我们可以像下面这样申明:

var nums by mutableStateListOf(mutableListOf(1, 2, 3))  // 有红线标注,写法错误// mutableStateListOf 是内部元素被观测,而不是它本身被观测,所以我们要把 `by` 换成 `=`
var nums = mutableStateListOf(mutableListOf(1, 2, 3))// `var` 也可以换成 `val`
val nums = mutableStateListOf(mutableListOf(1, 2, 3))// mutableStateListOf 本身就代表一个可观测的 List,所以 mutableListOf 也可以去除
val nums = mutableStateListOf(1, 2, 3)  // 这就是最终的写法

这个时候我们就可以优化下代码了:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val nums = mutableStateListOf(1, 2, 3)setContent {Column {Button(onClick = {nums.add(nums.last() + 1)}) {Text("List 加 1")}for (num in nums) {Text("第 $num 块文字")}}}}
}

运行:

在这里插入图片描述

提到 List,我们就会想到 Map,同样 Map 也提供了一个 mutableStateMapOf!它也可以观测到内部 Map 的数据变化!

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val maps = mutableStateMapOf(1 to "One", 2 to "Two")setContent {Column {Button(onClick = {maps[3] = "Three"}) {Text("Maps 加 1")}for ((key, value) in maps) {Text("$key 对应 value: $value")}}}}
}

运行:

在这里插入图片描述


http://www.ppmy.cn/news/1264304.html

相关文章

C语言 占位符 + 转义字符 + ASCLL 表 + 缓冲区

整型 占位符 取值范围 浮点型 占位符 转义字符 注意&#xff1a;绿色字体标注的为不可打印字符。 printf 常用占位符&#xff1a; printf 附加格式 ASCII 表 标准ASCII码的范围是0&#xff5e;127&#xff0c;只需7位二进制数即可表示。 缓冲区 缓冲区分类&#xff1a; sizeof(…

基于 Linux 内核驱动模块的简介

基于 Linux 内核驱动模块的简介 最简内核驱动原理 内核编程的最简单表现就是内核模块&#xff0c; 它可以作为一段可动态加载的成熟的内核级的代码使用。使用时一般不限制模块个数和类型&#xff0c;即插即用&#xff0c; 高效快捷、 性能稳定。缺点为性能和内存利用缺失&#…

生日备忘录(自定义排序)

题目&#xff1a; 实现简易的生日备忘录。温暖的亲情是前进的动力&#xff0c;别忘记在家人生日时送 去祝福。定义类 Member 描述家庭成员&#xff0c;每位家人有自己的姓名 name 和出生日期 birthday&#xff0c;出生日期应描 述为由 year、month、day 组成的 Date 类。在 ma…

xml文本转Java对象

Java对象转String public static String toData(Object object) throws Exception {JAXBContext jc JAXBContext.newInstance(object.getClass());Marshaller m jc.createMarshaller();StringWriter output new StringWriter(2048);m.marshal(object, output);String data …

如何访问内部网络做内网穿透

项目&#xff1a;https://github.com/ehang-io/nps 有个公网服务器&#xff0c;搭建服务端。 然后客户端使用&#xff1a; -server是服务端的访问方式。-vkey是秘钥。 ./npc -server192.227.19.12:8024 -vkeyoies8gq3wml -typetcp然后在服务端配置TCP隧道即可。

老有所依:TSINGSEE青犀养老院智能视频监管方案

养老院智能监控方案是为了提高养老院内老人的安全和护理质量&#xff0c;利用智能技术与监控设备进行全方位的监控和管理&#xff0c;可以加强对老人的监护和护理&#xff0c;提高养老院的服务质量和安全性。 旭帆科技基于视频技术与AI智能分析技术构建的养老院智能视频监控方…

为什么要用向量检索

之前写过一篇文章&#xff0c;是我个人到目前阶段的认知&#xff0c;所做的判断。我个人是做万亿级数据的搜索优化工作的。一直在关注任何和搜索相关的内容。 下一代搜索引擎会什么&#xff1f;-CSDN博客 这篇文章再来讲讲为什么要使用向量搜索。 在阅读这篇文章之前呢&#xf…

python+pytest接口自动化(12)-自动化用例编写思路 (使用pytest编写一个测试脚本)

经过之前的学习铺垫&#xff0c;我们尝试着利用pytest框架编写一条接口自动化测试用例&#xff0c;来厘清接口自动化用例编写的思路。 我们在百度搜索天气查询&#xff0c;会出现如下图所示结果&#xff1a; 接下来&#xff0c;我们以该天气查询接口为例&#xff0c;编写接口测…