聊聊 Jetpack Compose 的 “状态订阅自动刷新” -- mutableStateListOf
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")}}}}
}
运行:

相关文章:
聊聊 Jetpack Compose 的 “状态订阅自动刷新” -- mutableStateListOf
Jekpack Compose “状态订阅&自动刷新” 系列: 【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - MutableState/mutableStateOf 】 【 聊聊 Jetpack Compose 的 “状态订阅&自动刷新” - - remember 和重组作用域 】 【 聊聊 Jetpack Compose 的 …...
Dockerfile详解#如何编写自己的Dockerfile
文章目录 前言编写规则指令详解FROM:基础镜像LABEL:镜像描述信息MAINTAINER:添加作者信息COPY:从宿主机复制文件到镜像中ADD:从宿主机复制文件到镜像中WORKDIR:设置工作目录 前言 Dockerfile是编写docker镜…...
Elasticsearch桶聚合和管道聚合
1. 根据名称统计数量 GET order/_search {"_source": false,"aggs": {"aggs_name": { // 自定义查询结果名称"terms": { // 使用的函数"field": "name.keyword"}}} }查询结果例子: "aggregat…...
联想范建平:联想混合AI架构具备两大明显优势
12月7日,首届AI PC创新论坛在北京联想集团总部举办。联想集团副总裁、联想研究院人工智能实验室负责人范建平表示,为提供真正可信、个性化的AI专属服务,联想提出了混合智能(Hybrid AI)概念,并已经显现出更强…...
探索Spring事件监听机制的奇妙世界
文章目录 什么是Spring事件监听机制主要组件内置的事件监听类自定义事件监听类总结 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 什么是Spring事件监听机制 Spring事件监听机制是Spr…...
什么是散列函数
散列函数是一种公开的数学函数。散列函数运算的输入信息也可叫作报文。散列函数运算后所得到的结果叫作散列码或者叫作消息摘要。散列函数具有如下一些特点: (1)不同内容的报文具有不同的散列码,而一旦原始报文有任何改变…...
tomcat反序列化
漏洞介绍: 漏洞名称: Apache Tomcat反序列化漏洞影响范围: Apache Tomcat服务器中使用了自带session同步功能的配置,且没有使用Encrypt Interceptor加密拦截器的情况下。漏洞描述: Apache Tomcat是一个基于Java的Web应用软件容器,用于运行servlet和JSP Web应用。当Tomc…...
flask 请求勾子实现 request_auth认证
from flask import g,request from comment.utils.tokens_pyjwt import verify_tokensdef jwt_request_auth():从请求(request)中获取token,并且验证token,验证成功之后把用户id保存到全局变量g中g.user_idNone #定义变量#前端代码是是把token携带请求头…...
【STM32入门】3.OLED屏幕
1.OLED引脚 OLED屏幕的接线按图所示,本例中用的是4管脚OLED屏幕 2.驱动程序 配套的驱动程序是“OLED.c",主要由以下函数构成:1、初始化;2、清屏;3、显示字符;4、显示字符串;5、显示数字…...
python圣诞树代码编程
以下是一个简单的Python圣诞树代码: def draw_tree(height): for i in range(height): print( * (height - i - 1) * * (2 * i 1)) print( * (height - 1) |)draw_tree(10) 这个函数会绘制一个等腰三角形,其中每一行的星号数量从1开…...
js数组删除某个元素
...
hbuilder + uniapp +vue3 开发微信云小程序
1、创建项目: 2、创建项目完成的默认目录结构: 3、在根目录新建一个文件夹cloudFns(文件名字随便),存放云函数源码: 4、修改manifest.json文件:添加 小程序 appid和cloudfunctionRoot࿰…...
服务器配置免密SSH
在当今互联网时代,远程工作和网络安全已成为信息技术领域的热点话题。无论是管理远程服务器、维护网络设备还是简单地从家中连接到办公室,安全始终是首要考虑的因素。这就是为什么 SSH(Secure Shell)成为了网络专业人士的首选工具…...
2023 开发人员生态系统现状信息图:《开发者生态系统现状报告》
本心、输入输出、结果 文章目录 2023 开发人员生态系统现状信息图:《开发者生态系统现状报告》前言目录细节软件开发者薪资趋势过去 3 年科技行业的性别分布 生成式 AI 服务的复杂格局开发者社区的心理健康花有重开日,人无再少年实践是检验真理的唯一标准…...
TCP协议实现一对一聊天
服务端代码: import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; /** * 发送消息线程 */ class…...
python使用conda管理多个环境
一、Anaconda简介 Anaconda 是专门为了方便使用 Python 进行数据科学研究而建立的一组软件包,涵盖了数据科学领域常见的 Python 库,并且自带了专门用来解决软件环境依赖问题的 conda 包管理系统。主要是提供了包管理与环境管理的功能,可以很方…...
实现个微机器人的二次开发
请求URL: http://域名地址/scanJoinRoom 请求方式: POST 请求头Headers: Content-Type:application/jsonAuthorization:login接口返回 参数: 参数名必选类型说明wId是string登录实例标识url是string…...
Android 记录一些Framework开发的命令
源码编译流程 1. "source build/envsetup.sh" (source可以用 . 代替,即". build/envsetup.sh") 2. "lunch",并选择要编译的项目或者"choosecombo" 3. "make idegen -j4" (这里的 -j4 表示用4线程来…...
Ant Design Vue 年选择器
文章目录 参考文档效果展示实现过程 参考文档 提示:这里可以添加本文要记录的大概内容: DatePicker 日期选择框 大佬:搬砖小匠(Ant Design vue 只选择年) 提示:以下是本篇文章正文内容,下面案…...
AGM CPLD 应用指南
AGM的部分料号跟Altera 硬件Pin to Pin兼容,映射关系表如下: EPM240T100CxN—>AG256SL100(CPLD) EPM240T100IxN---->AG256SL100(CPLD) EPM570T100CxN—>AG576SL100(CPLD) EPM570T100IxN---->AG576SL100(CPLD) EPM570T144CxN—>AG576SL144…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
