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

news2024/9/25 9:38:56

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

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

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

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

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


讲任何一个新的主题或者知识点,习惯性的从 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.coloradmin.cn/o/1284927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

非标设计之气缸类型

空压机&#xff1a; 空压机又称空气压缩机&#xff0c;简单来说就是将机械能转化为压力能来进行工作的&#xff0c;空压机在电力行业应用比较多&#xff0c;除了在电力行业应用较多外&#xff0c;其实空压机还有一个比较常见的用途就是用来制冷和分离气体&#xff0c;输送气体…

java SSM毕业生信息管理myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

前言 学校的规模不断扩大&#xff0c;学生数量急剧增加&#xff0c;有关学生的各种信息量也成倍增长。面对庞大的信息量需要有学生信息管理系统来提高学生管理工作的效率。通过这样的系统可以做到信息的规范管理、科学统计和快速查询、修改、增加、删除等&#xff0c;从而减少管…

【力扣热题100】207. 课程表 python 拓扑排序

【力扣热题100】207. 课程表 python 拓扑排序 写在最前面207. 课程表解决方案&#xff1a;判断是否可以完成所有课程的学习方法&#xff1a;拓扑排序实现步骤Python 实现性能分析结论 写在最前面 刷一道力扣热题100吧 难度中等 https://leetcode.cn/problems/course-schedule…

[leetcode ~二叉树] 模版

文章目录 1. 左叶子之和2. 翻转二叉树 E 1. 左叶子之和 :::details 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&…

Proteus仿真--基于1602LCD与DS18B20设计的温度报警器

本文介绍基于1602LCD与DS18B20设计的温度报警器设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中温度传感器选用DS18B20器件&#xff0c;主要用于获取温度数据并上传&#xff0c;温度显示1602LCD液晶显示器&#xff0c;报警模块选用蜂鸣器&#…

隐写术和人工智能

在一项新的研究中&#xff0c;人工智能对齐研究实验室 Redwood Research 揭示了大型语言模型 (LLM) 可以掌握“编码推理”&#xff0c;这是一种隐写术形式。 这种有趣的现象使得大型语言模型能够以人类读者无法理解的方式巧妙地将中间推理步骤嵌入到生成的文本中。 大型语言…

从零开始学习 JS APL(五):完整指南和实例解析

目录 学习目标&#xff1a; 学习内容&#xff1a; 学习时间&#xff1a; 学习内容&#xff1a; Window对象&#xff1a; 定时器-延时函数&#xff1a; JS 执行机制&#xff1a; location对象&#xff1a; 本地存储&#xff1a; 本地存储分类- localStorage&#xff1a…

Fiddler移动端抓包

本篇文章&#xff0c;博主想使用通俗易懂的话语&#xff0c;让大家明白以下内容&#xff1a; 什么是抓包哪些场景需要用到抓包Fiddler抓包的原理怎样使用Fiddler进行移动端抓包 抓包 包 (Packet) 是TCP/IP协议通信传输中的数据单位&#xff0c;一般也称“数据包”。 我们平常…

uniapp 云打包 生成安卓证书文件

现在使用uniapp来开发小程序&#xff0c;H5&#xff0c;APP越来越多了&#xff0c;目前开发了一款APP&#xff0c;使用的也是uniapp。在此记录下用uniapp开发app云打包时约到的一些问题吧。 前因是我司安卓同学休产假&#xff0c;像云打包时需要的证书文件只能自己动手来搞。看…

ai绘画Midjourney绘画提示词Prompt教程

一、Midjourney绘画工具 SparkAi【无需魔法使用】&#xff1a; SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01;本系统使用NestjsVueTypescript框架技术&#xff0c;持续集成AI能力到…

【计算机网络笔记】物理层——物理介质

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

element-ui upload组件中将file文件数据转成二进制流数据格式

方法一 handleBeforeUpload (file)const reader new FileReader()reader.readAsArrayBuffer(file)reader.onload async function (theFile) {const binary new Blob([theFile.target.result]) // 转成二进制流数据 即binary数据格式}}方法二 const aBlob new Blob([file],…

阿里云租赁费用_阿里云服务器多配置报价表

阿里云服务器租用费用&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、轻量应用服务器2核2G3M带宽轻量服务器一年87元&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;ECS云服务器e系列2核2G配置99元一年、2核4G配置365元一年、2核8G配置522元一年…

[HTML]Web前端开发技术7(HTML5、CSS3、JavaScript )CSS的定位机制——喵喵画网页

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;佬佬会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

3 开发环境搭建

一、Ubuntu和Windows文件互传 ① 开启Ubuntu的FTP服务&#xff1a; 下载vsftpd&#xff1a;sudo apt-get install vsftpd; 打开vsftpd.conf&#xff1a;sudo nvim /etc/vsftpd.conf; 确保这两行代码没有被注释&#xff1a; 之后重启FTP服务&#xff1a; ② Windows下载FTP客…

Windows中使用ScreenToGif进行Gif录屏

文章目录 一、前言二、下载ScreenToGif2.1、官网2.2、Github下载 三、使用ScreenToGif3.1、安装打开3.2、录像机3.3、录屏3.4、保存Gif动图3.5、保存完成 四、最后 一、前言 之前写文章时需要录制网页操作效果&#xff0c;使用的是谷歌的扩展插件【镀铬捕获】&#xff0c;后面…

【Java 基础】19 多线程基础

文章目录 进程和线程进程&#xff08;Process&#xff09;线程&#xff08;Thread&#xff09; 线程的创建1&#xff09;继承 Thread 类2&#xff09;实现 Runnable 接口3&#xff09;使用 Lambda 表达式4&#xff09;总结 线程的状态状态的分类状态间转换 多线程是一种 同时执…

【React + Typescript】使用WebPack包管理、各种扩展插件组成的初始模板,开源协议:CC-BY-4.0

React Typescript Webpack 模板 模板展示项目结构使用的部分扩展包页面配置代码Layout 公共容器组件路由Jspackage.json 开源模板下载TIP 模板展示 项目结构 使用的部分扩展包 &#x1f4c2; System ├── &#x1f4c2; Plugin │ ├── &#x1f4c4; file-loader | 在处…

基于AWS Serverless的Glue服务进行ETL(提取、转换和加载)数据分析(二)——数据清洗、转换

2 数据清洗、转换 此实验使用S3作为数据源 ETL: E extract 输入 T transform 转换 L load 输出 大纲 2 数据清洗、转换2.1 架构图2.2 数据清洗2.3 编辑脚本2.3.1 连接数据源&#xff08;s3&#xff09;2.3.2. 数据结构转换2.3.2 数据结构拆分…