聊聊 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…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
