降Compose十八掌之『鸿渐于陆』| State

公众号「稀有猿诉」        原文链接 降Compose十八掌之『鸿渐于陆』| State

Jetpack Compose是一种声明式的UI框架,用以构建GUI应用程序。通过前面的文章我们学会了如何使用元素来填充页面,也学会了如何装饰元素,但这还不够。UI还必须处理与页面直接相关的数据,因为这是对用户有价值的东西。今天就来学习一下Compose如何处理数据。

什么是状态

状态(State)其实就是数据,Compose是一种UI框架,UI要显示数据才会有价值。但是呢,Compose毕竟是一种UI框架,它应该只处理需要展示给用户的那部分数据,所以,这里说的数据应该是经过业务逻辑处理过的,需要展示给用户的那部分数据。也就是说只需要处理从ViewModel推送过来的数据即可。

此外,还有一部分只需要在UI内部处理的数据,比如像一些控件的状态,动画中的参数变化等等,这些数据需要完全在UI部分处理掉,都不应该暴露给ViewModel。

因此,对于Compose来说的状态(State),就包括两部分,一部分是从ViewModel推过来的需要展示的数据(具体叫做UiState),以及UI内部逻辑中的状态。

状态与重组

本质上来说Compose就是坨函数,更新UI的方式就变成了用新的参数来重新调用这些函数。这些参数便是状态了。任何时候状态发生变化就会发生重组(re-Composition),结果就是UI刷新了,最新的数据呈现给了用户。感知状态变化如何影响着UI的刷新就是状态管理。

有些术语需要说明一下:组合(Composition)描述着UI的生成过程,也即当Compose执行我们所声明的一坨坨函数的时候;初始组合(Initial Composition)首次执行这一坨函数的过程;重组(re-Composition)当状态有更新,重新运行某些函数的过程。

UI要想刷新,呈现最新的数据,这就需要Compose进行重组,而重组是由状态更新触发的,也就是说我们需要用新的数据来重新执行这一坨函数。对于业务逻辑数据,这很好办,可以通过ViewModel推送新的数据,然后重新调用UI函数即可。但这并没有看起来那么容易,因为ViewModel与UI的关系通常不是ViewModel直接持有着UI的对象或者函数,更多的时候是Compose的函数(Composable)中创建持有ViewModel对象,一个函数是没有办法直接调用自身的,这会陷入死循环的(StackOverFlow)。

@Composable
fun WellnessScreen(
    modifier: Modifier = Modifier,
    wellnessViewModel: WellnessViewModel = viewModel()
) {
    Column(modifier = modifier) {

        WellnessTasksList(
            list = wellnessViewModel.tasks,
            onCheckedTask = { },
            onCloseTask = { }
        )
    }
}

对于UI逻辑中的数据也是如此,比如说,一个很简单的按扭计数,按照常规的理解,似乎可以这样写:

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
    var count = 0

    Column(modifier = modifier.padding(16.dp)) {
        Text(
            text = "You have had $count glasses.",
            modifier = modifier.padding(16.dp)
        )
        Row(
            modifier = modifier.padding(top = 8.dp),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Button(onClick = { count++ }, enabled = count < 10) {
                Text("Add one")
            }
            Button(onClick = { count = 0 }, Modifier.padding(start = 8.dp), enabled = count > 0) {
                Text("Clear water count")
            }
        }
    }
}

但这样写文本中的数字不会变化。

重组要想发生,就必须重新调用Compose的『根函数』,这就需要用到专门的数据结构MutableState,Compose会识别并跟踪这些State,当其变化时,会触发重组,并使用State中的最新值。

interface MutableState<T> : State<T> {
    override var value: T
}

管理UI状态

要想让Compose识别到数据变化,就需要使用状态State,这样当数据变化时会触发重组,Compose会用State中的最新数值来重新运行函数,以刷新UI。比如上面的计数的例子,可以这样修改:

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
    var count by remember { mutableStateOf(0) }
    // Other codes not changed
}

这次,能得到期望的行为:

state.gif

有三种方式声明一个状态MutableState:

  • val state = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (vale, setValue) = remember { mutableStateOf(default) }

基本上无差别,一般委托方式用的稍多一些。这里remember的作用是让Compose记住并追踪状态的变化。如果想要让状态能够跨Activity的实例(比如遇到屏幕旋转,语言变化等配置变化导致Activity重启)就需要用remeberSaveable。

这些主要是针对Composable中内部的状态。对于像从ViewModel过来的业务数据,一般都用collectAsState系列方法。

有状态(Stateful)和无状态(Stateless)

对于包含了创建State的函数就称作有状态的Composable,而不包含创建状态就是Stateless的。

无状态的Composable是幂等的,调用时直接传入数据,不会产生副作用,也不会触发重组,显然这对开发者来说是最高效的,因为很纯粹,使用起来相当简单,并且完全可复用,应该尽可能的创建并使用无状态Composables

@Composable
fun CustomButton(text: String, onClick: ()->Unit) {
     Button(onClick) {
         Text(text)
     }
}

状态提升

因为State是有额外的成本的,因此应该尽可能的减少State的创建,那么就要尽可能的复用State。这就需要把状态提升到使用此State的所有子函数的最小公共函数里面。比如前面的例子,状态count在Text和两个Button中都有使用,那么count至少要提升到它们的公共函数里面。假如,这个count在其他Composable中也有使用,那么就提升到WaterCounter的更上一层,甚至是整个Screen级别。

一般情况下,除了一些仅在局部使用的状态外,放在页面级别的根函数里面是比较好的选择,这样的话只有页面的根函数是Stateful的,其余函数都是Stateless的。

实战

纸上来行终觉浅,要想掌握还是要亲手撸。状态管理对于UI框架是相当重要的,因为这是UI发挥作用和产生价值的地方。对于状态管理有一些非常好的CodeLab,可以亲手撸一下,感受一下状态管理到底是啥。

  • State in Jetpack Compose

参考资料

  • State and Jetpack Compose
  • How to handle state in Jetpack Compose
  • Everything you need to know about State in Jetpack Compose with examples

subscription

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

保护原创,请勿转载!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/791722.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【VUE实战项目】使用typescript重构项目

前言 本文是博主vue实战系列中的一篇&#xff0c;本系列不是专业的前端教程&#xff0c;是专门针对后端熟悉前端的。前面我们用vue实现了一个基础的管理系统&#xff0c;前文专栏地址&#xff1a; https://blog.csdn.net/joker_zjn/category_12469788.html?spm1001.2014.300…

windows10开启防火墙,增加入站规则后不生效,还是不能访问后端程序

一、背景&#xff1a; 公司护网要求开启防火墙&#xff0c;开启防火墙后&#xff0c;前后端分离的项目调试受影响&#xff0c;于是增加入站规则开放固定的后台服务端口&#xff0c;增加的mysql端口3306和redis端口6379&#xff0c;别人都可以访问&#xff0c;但是程序的端口808…

丑数问题,力扣264,坑点

丑数问题&#xff0c;力扣264&#xff0c;坑点 力扣链接 给你一个整数 n &#xff0c;请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;12 解释&#xff1a;[1, 2, 3, 4, 5, 6, 8, 9, …

f_mkfs格式化最小分区数是191

使用fatfs的f_mkfs最小分区数是191原因&#xff1a; 在挂载ram_disk时参考的文章有提到&#xff1a; “然后是GET_SECTOR_COUNT 用于f_mkfs格式化时获取可用的sector的数量&#xff0c;32bit-LBA的情况下至少为191” 自己也实际试过确实要不少于191&#xff0c;网上也没找到相…

将 Vision Transformer 用于医学图像的语义分割

关于ViT的关键点如下&#xff1a; ViT架构基于将图像表示为一组补丁。图像补丁是图像的非重叠块。每个块最初都有一个由该块中的图像像素形成的嵌入向量。Transformer编码器是ViT的主要部分&#xff0c;它根据它们的类别归属来训练补丁之间的相似度。它包含一系列线性、归一化…

DLP迎来新机遇 | 天空卫士数据防泄漏防护市场占有率连续三年第一

IDC 于近日发布了《中国数据泄露防护市场份额&#xff0c;2023&#xff1a;DLP迎来新机遇》&#xff08;Doc#CHC50973524 &#xff0c;2024年6月&#xff09;报告&#xff0c;天空卫士DLP产品以21.9%的市场份额再次位列中国数据防泄露防护市场第一。这一成绩体现了天空卫士在技…

2024SpringCloud学习笔记

远程调用Rest Template 服务注册与发现&分布式配置管理 Consul 下载安装 官网https:/ldeveloper.hashicorp.com/consul/downloads 开发者模式启动consul agennt -dev 浏览器访问本地端口:8500 服务注册与发现 Maven引入 <!--SpringCloud consul discovery -->…

基于 BERT 的非结构化领域文本知识抽取

文章目录 题目摘要方法实验 题目 食品测试的大型语言模型 论文地址&#xff1a;https://arxiv.org/abs/2103.00728 摘要 随着知识图谱技术的发展和商业应用的普及&#xff0c;从各类非结构化领域文本中提取出知识图谱实体及关系数据的需求日益增加。这使得针对领域文本的自动化…

MechMind结构光相机 采图SDK python调用

测试效果 Mech-Mind结构光相机 Mech Mind(梅卡曼德)的结构光相机,特别是Mech-Eye系列,是工业级的高精度3D相机,广泛应用于工业自动化、机器人导航、质量检测等多个领域。以下是对Mech Mind结构光相机的详细解析: 一、产品概述 Mech Mind的结构光相机,如Mech-Eye PRO,…

极狐GitLab X 中科星图,用实际行动赋能空天行业开发者

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…

RFID技术革新养猪业,构建智能化养殖场

RFID技术作为无线射频识别技术的一种&#xff0c;凭借着非接触、高效识别的特性&#xff0c;在养殖业行业中得到了广泛的应用&#xff0c;为构建智能化、高效化的养殖场提供了强大的技术支持&#xff0c;给传统养殖业带来了一场前所未有的技术变革。以下是RFID技术在养猪行业不…

podman 替代 docker ? centos Stream 10 已经弃用docker,开始用podman了!

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

记一次Ueditor上传Bypss

前言 前一段时间和小伙伴在某内网进行渗透测试&#xff0c;目标不给加白&#xff0c;只能进行硬刚了&#xff0c;队友fscan一把梭发现某资产疑似存在Ueditor组件&#xff0c;但初步测试是存在waf和杀软的&#xff0c;无法进行getshell&#xff0c;经过一番折腾最终getshell&am…

Python基础语法:运算符详解(算术运算符、比较运算符、逻辑运算符、赋值运算符)②

文章目录 Python中的运算符详解一、算术运算符二、比较运算符三、逻辑运算符四、赋值运算符五、综合示例结论 Python中的运算符详解 在Python编程中&#xff0c;运算符用于执行各种操作&#xff0c;例如算术计算、比较、逻辑判断和赋值。了解并掌握这些运算符的使用方法是编写…

【观成科技】Websocket协议代理隧道加密流量分析与检测

Websocket协议代理隧道加密流量简介 攻防场景下&#xff0c;Websocket协议常被用于代理隧道的搭建&#xff0c;攻击者企图通过Websocket协议来绕过网络限制&#xff0c;搭建一个低延迟、双向实时数据传输的隧道。当前&#xff0c;主流的支持Websocket通信代理的工具有&#xf…

物联网系统中市电电量计量方案(二)

上文我们主要介绍了电量计量中最重要的组成部分——电量计量芯片&#xff08;如果没有阅读该文章的&#xff0c;可以点击这里&#xff09;。本文会再为大家介绍电量计量的另外一个组成部分——电流互感器。 电流互感器的定义 电流互感器是一种可将一次侧大电流转换为二次侧小电…

AppStandby白名单机制

背景&#xff1a;原生机制中AppStandby机制的白名单是共享Doze白名单&#xff0c;即一旦设置doze白名单&#xff0c;也即AppStandby的白名单 需求&#xff1a;如何建立AppStandby自己的白名单 技术原理&#xff1a;可以使用setAppStandbyBucket接口实现 setAppStandbyBucket…

Java内存划分详解:从基础到进阶

Java内存划分详解&#xff1a;从基础到进阶 1. 程序计数器&#xff08;Program Counter Register&#xff09;2. Java虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;3. 堆&#xff08;Heap&#xff09;4. 方法区&#xff08;Method Area&#xff09;5. 运行时常量…

揭秘”大模型加速器”如何助力大模型应用

文章目录 一、大模型发展面临的问题二、“大模型加速器”助力突破困难2.1 现场效果展示2.1.1 大模型加速器——文档解析引擎2.2.2 图表数据提取 三、TextIn智能文档处理平台3.1 在线免费体验3.1.1 数学公式提取3.1.2 表格数据提取 四、acge文本向量化模型4.1 介绍4.2 技术创新4…

前端面试题40(浅谈MVVM双向数据绑定)

MVVM&#xff08;Model-View-ViewModel&#xff09;架构模式是一种用于简化用户界面&#xff08;UI&#xff09;开发的软件架构设计模式&#xff0c;尤其在现代前端开发中非常流行&#xff0c;例如在使用Angular、React、Vue.js等框架时。MVVM模式源于经典的MVC&#xff08;Mod…