Compose 状态管理

文章目录

  • Compose 状态管理
    • 概述
    • 使用
      • MutableState
      • remember
      • StatelessComposable & StatefulComposable
      • 状态提升
      • rememberSaveable
        • 支持parceable
        • 不支持parceable
    • 使用ViewModel
      • ViewModelProvider.Factory
    • 使用Flow

Compose 状态管理

概述

当应用程序的状态发生变化时,Compose会进行重组,重组过程中会运行可能已更改的可组合项以响应状态变化,然后Compose会更新组合以反映所有更改。这就是Compose的工作流程。

使用

MutableState

val counter: MutableState<Int> = mutableStateOf(0)

这里的 counter 是一个MutableState<Int>类型,可以使用 .value 进行读写。

// 解构
val (counter: Int, setCounter: (Int) -> Unit) = mutableStateOf(0)

这里的 counter 是一个 Int 类型的数据,其他地方可以直接访问,需要更新 counter 时可以使用 setCounter(xx) 完成。

// 属性代理
val counter: Int by mutableStateOf(0)

这里的 counter 的读写会通过 getValue 和 setValue这 两个运算符的重写最终代理为对 value 的操作,通过 by 关键字,可以像访问一个普通的Int变量一样对状态进行读写。

remember

  • mutableStateOf() 函数会创建一个 MutableState 类型的对象,MutableState 是可观察类型,在值发生变化的情况下系统会安排重组持有该值的可组合函数。
  • remember() 在Composable首次执行时,remember中计算得到的数据会自动缓存,当Composable重组再次执行到remember处会返回之前已缓存的数据,无须重新计算。
@Preview
@Composable
fun Test3() {val index = remember {mutableStateOf(10)}Column(modifier = Modifier.fillMaxSize()) {Button(onClick = {index.value++Log.e("TAG", "点击了 :${index.value}")}) {Text("Click")}Text("${index.value}", fontSize = 30.sp)}
}

说明:使用 remember { mutableStateOf() } 记录状态,当 index 发生变化时,Text显示的值也会跟着发生变化。

StatelessComposable & StatefulComposable

StatelessComposable 不管理任何状态,它的输出仅取决于输入参数。它是无状态的,每次调用都会重新计算输出,并且不会记住之前的状态。

例如,一个简单的文本显示组件可以是一个 StatelessComposable

@Composable
fun MyTextComponent(text: String) { // StatelessComposableText(text = text)
}

说明:MyTextComponent 是一个 StatelessComposable,它接受一个字符串参数 text,并将其显示为文本。

StatefulComposable 会管理状态,内部持有或访问状态,并根据状态的变化来更新输出。

例如,一个计数器组件可以是一个 StatefulComposable

@Composable
fun MyCounterComponent() { // StatefulComposablevar count by remember { mutableStateOf(0) }Button(onClick = { count++ }) {Text(text = "计数器:$count")}
}

说明:MyCounterComponent 是一个 StatefulComposable,它内部使用 mutableStateOf 来管理一个整数状态 count。当点击按钮时,count 会增加 1,并更新计数器的显示。

StatelessComposable 更简单、高效,适用于不需要管理状态的组件,Stateless是一个“纯函数”,参数是变化的唯一来源,参数不变UI就不会变化。因此Compose编译器针对其进行了优化;而 StatefulComposable 更灵活、强大,适用于需要管理状态的组件。StatelesComposable 的重组只能来自上层 Composable 的调用,而 StatefulComposable 的重组来自其依赖状态的变化。

状态提升

状态提升也就是将 Statefule 改造为 Stateless,Stateless 由于不耦合任何业务逻辑,所以功能更加纯粹,相对于 Stateful 的可复用性更好,对测试也更加友好。

在这里插入图片描述

// StatefulComposable
@Composable
fun CounterScreen() {var counter by remember { mutableStateOf(0) }CounterComponent(counter = counter,onIncrement = { counter++ },onDecrement = {if (counter > 0) {counter--}})
}// StatelessComposable
@Composable
fun CounterComponent(counter: Int,onIncrement: () -> Unit,onDecrement: () -> Unit,
) {Column(modifier = Modifier.padding(16.dp)) {Text("Counter:", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)Text("$counter",modifier = Modifier.fillMaxWidth(),textAlign = TextAlign.Center,fontWeight = FontWeight.Bold)Row {Button(onClick = { onDecrement() }, modifier = Modifier.weight(1F)) {Text("-")}Button(onClick = { onIncrement() }, modifier = Modifier.weight(1F)) {Text("+")}}}
}

说明:CounterComponent经状态上提后,职责更加单一。

rememberSaveable

rememberSaveable() 函数可以保证ConfigurationChanged事件发生时(如屏幕旋转等)保存状态,数据会随onSaveInstanceState进行保存。

并在进程或者Activity重建时根据key恢复到对应的Composable中,这个key就是Composable在编译期被确定的唯一标识。因此当用户手动退出应用时,rememberSavable中的数据才会被清空。

rememberSavable实现原理实际上就是将数据以Bundle的形式保存,所以凡是Bundle支持的基本数据类型都可以自动保存。对于一个对象类型,则可以通过添加@Parcelize变为一个Parcelable对象进行保存。

支持parceable

添加kotlin-parcelize插件:

plugins {id 'kotlin-parcelize'
}

使用:

// 定义数据类
@Parcelize
data class Person(val name: String, val age: Int) : Parcelable// 使用:
@Composable
fun PersonScreen() {var person by rememberSaveable { mutableStateOf(Person("小白", 18)) }Button(onClick = { person = Person("小黑", 28) }) {Text(person.toString())}
}
不支持parceable

有的数据结构可能无法添加Parcelable接口,比如定义在三方库的类等,此时可以通过自定义Saver为其实现保存和恢复的逻辑。只需要在调用rememberSavable时传入此Saver。

// 定义数据类
data class People(val name: String, val age: Int)// 定义Saver
object PersonSaver : Saver<People, Bundle> {override fun restore(value: Bundle): People {val name = value.getString("name") ?: ""val age = value.getInt("age")return People(name, age)}override fun SaverScope.save(value: People): Bundle {return Bundle().apply {putString("name", value.name)putInt("age", value.age)}}
}// 使用:
@Composable
fun PeopleScreen() {var people by rememberSaveable(stateSaver = PersonSaver) { mutableStateOf(People("小红", 18)) }Button(onClick = { people = People("小绿", 28) }) {Text(people.toString())}
}

支持MapSaver:

MapSaver将对象转换为 Map<String, Any> 的结构进行保存。

data class City(val name: String, val country: String)// 定义MapSaver
val CitySaver = run {val nameKey = "name"val countryKey = "country"mapSaver(save = { mapOf(nameKey to it.name, countryKey to it.country) },restore = { City(it[nameKey] as String, it[countryKey] as String) })
}@Composable
fun CityScreen() {var city by rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("北京", "中国")) }Button(onClick = { city = City("东京", "日本") }) {Text(city.toString())}
}

支持ListSaver:

ListSaver则是将对象转换为 List<Any> 的数据结构进行保存。

val CitySaver2 = listSaver<City, Any>(save = { listOf(it.name, it.country) },restore = { City(it[0] as String, it[1] as String) }
)

使用ViewModel

在 Compose 中也可以使用 ViewModel 缓存状态,通过 LiveData 或 Flow 监听变化。

添加依赖:

implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"

使用:

class CountViewModel : ViewModel() {private val _count = MutableLiveData(10)val count get() = _countfun onCountChanged(count: Int) {_count.postValue(count)}
}
@Composable
fun MyCount() {val viewModel: CountViewModel = viewModel()val count by viewModel.count.observeAsState(0) // 语法糖Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text("$count")Button(onClick = { viewModel.onCountChanged(count + 2) }) {Text("点击")}}
}

说明:通过 observeAsState() 函数观察 LiveData 对象,当 LiveData 发生变化时,该对象都会更新。

ViewModelProvider.Factory

class CountViewModel(defaultCount: Int) : ViewModel() {private val _count = MutableLiveData(defaultCount)val count get() = _countfun onCountChanged(count: Int) {_count.postValue(count)}
}class CountViewModelFactory(private val defaultCount: Int) : ViewModelProvider.Factory {override fun <T : ViewModel> create(modelClass: Class<T>): T {return CountViewModel(defaultCount) as T}
}
@Composable
fun MyCount() {val viewModel: CountViewModel = viewModel(factory = CountViewModelFactory(100))val count by viewModel.count.observeAsState(0)Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text("$count")Button(onClick = {viewModel.onCountChanged( count+ 2) }) {Text("点击")}}
}

使用Flow

class CountViewModel : ViewModel() {private val _countFlow = MutableStateFlow(10)val countFlow get() = _countFlow.asStateFlow()fun onCountChanged(count: Int) {_countFlow.value = count}
}
@Composable
fun MyCount() {val viewModel: CountViewModel = viewModel()val count: Int by viewModel.countFlow.collectAsState(0) // 语法糖Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text("$count")Button(onClick = {viewModel.onCountChanged(count + 2)}) {Text("点击")}}
}

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

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

相关文章

(40)4.30数据结构(队列)

1.队列的基本概念 2.队列的顺序 #define MaxSize 10 #define ElemType int typedef struct { ElemType data[MaxSize]; int front, rear; }SqQueue;//1.初始化操作 void InitQueue(SqQueue& Q) { //初始化 队头&#xff0c;队尾指针指向0 Q.rear Q.fron…

环形链表理解||QJ141.环形链表

在链表中&#xff0c;不光只有普通的单链表。之前写过的的一个约瑟夫环形链表是尾直接连向头的。这里的环形链表是从尾节点的next指针连向这链表的任意位置。 那么给定一个链表&#xff0c;判断这个链表是否带环。qj题141.环形链表就是一个这样的题目。 这里的思路是用快慢指…

jenkins+gitlab+ansible-tower实现发布

前提准备&#xff1a; gitlab中上传相应的jenkinsfile文件和源码。 安装和破解ansible-tower。 安装jenkins。 大致流程&#xff1a;从gitlab中拉取文件&#xff0c;存放到windows机器上&#xff0c;使用nuget等进行打包到windows中&#xff0c;使用sshPublisher语句传输到远程…

LLMs之RAG:LangChain-Chatchat(一款中文友好的全流程本地知识库问答应用)的简介(支持 FastChat 接入的ChatGLM-2/LLaMA-2等多款主流LLMs+多款embe

LLMs之RAG&#xff1a;LangChain-Chatchat(一款中文友好的全流程本地知识库问答应用)的简介(支持 FastChat 接入的ChatGLM-2/LLaMA-2等多款主流LLMs多款embedding模型m3e等多种TextSplitter分词器)、安装(镜像部署【AutoDL云平台/Docker镜像】&#xff0c;离线私有部署支持RTX3…

yum仓库及NFS共享

yum简介 yum是一个基于RPM包&#xff08;是Red-Hat Package Manager红帽软件包管理器的缩写&#xff09;构建的软件更新机制&#xff0c;能够自动解决软件包之间的依赖关系。 yum 实现过程 先在yum服务器上创建 yum repository&#xff08;仓库&#xff09;&#xff0c;在仓…

stm32f103zet6_DAC_2_输出电压

实现效果 DAC输出的电压 同过电压表测量电压 1.DAC配置的步骤 初始化DAC时钟。配置DAC的GPIO端口。设置DAC的工作模式&#xff08;例如&#xff0c;是否使用触发功能&#xff0c;是否启用DAC中断等&#xff09;。启动DAC。 2常用的函数 函数 HAL_DAC_Start() - 开启指定…

EtherCAT开发_4_分布时钟知识点摘抄笔记1

分布时钟 (DC&#xff0c;Distributed Cl ock) 可以使所有EtherCAT设备使用相同的系统时间&#xff0c;从而控制各设备任务的同步执行。从站设备可以根据同步的系统时间产生同步信号&#xff0c;用于中断控制或触发数字量输入输出。支持分布式时钟的从站称为 DC 从站。分布时钟…

ATA-2161高压放大器用途有哪些种类

高压放大器是一种电子设备&#xff0c;其主要功能是将输入信号放大到较高的电压水平&#xff0c;同时保持信号的形状和特性。这种设备在各种应用领域中都有重要作用&#xff0c;它的种类繁多&#xff0c;根据不同的用途可以分为多种类型。 1.医学领域 在医学设备中&#xff0c;…

Sermant在异地多活场景下的实践

Sermant社区在1.3.0和1.4.0版本相继推出了消息队列禁止消费插件和数据库禁写插件&#xff0c;分别用于解决异地多活场景下的故障切流和保护数据一致性问题。本文将对Sermant在异地多活场景下的实践进行剖析。 一、异地多活 1.1 什么是异地多活 对于一个软件系统&#xff0c;…

极致视觉盛宴,尽在Extreme Picture Finder!

在信息爆炸的时代&#xff0c;网络图片如同繁星点点&#xff0c;为我们的生活增添无尽的色彩。然而&#xff0c;如何在浩渺的网海中快速、准确地找到心仪的图片&#xff0c;却成了许多人的难题。此刻&#xff0c;Extreme Picture Finder如同一位贴心的向导&#xff0c;引领我们…

idea修改maven项目名称及子模块名称

一、修改目录名称 shift F6修改目录&#xff0c;选择“rename module and dictionary”。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/43efd9c6af6e43ad9656455db94b37a2.png)二、修改子项目pom的 三、修改父项目pom的 四、刷新maven项目

effective python学习笔记_pythonic思维

查py版本 import sys sys.version sys.version_info 遵循PEP8 PEP8,Python Enhancement Proposal #8 章节列了几个点&#xff0c;也可以看原文PEP 8 – Style Guide for Python Code | peps.python.org 导包顺序建议&#xff1a;先导标准库模块&#xff0c;再导三方模块&…

论文查重率高,有什么办法降重吗?推荐笔灵AI

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下&#xff0c;延毕的威胁可能就在眼前。对于即将告别校园的学子们&#xff0c;这无疑是个噩梦。四年磨一剑&#xff0c;谁也不想在最后关头功亏一篑。 查重率过高&#xff0c;无非以下两种原因。要么是作为“…

LMdeploy推理实践

在inter-studio平台上&#xff0c;下载模型&#xff0c;体验lmdeploy 下载模型 这里是因为平台上已经有了internlm2模型&#xff0c;所以建立一个符号链接指向它&#xff0c;没有重新下载 ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b /root/如…

论文查重率高,有什么办法降重吗?推荐几个ai降重工具

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下&#xff0c;延毕的威胁可能就在眼前。对于即将告别校园的学子们&#xff0c;这无疑是个噩梦。四年磨一剑&#xff0c;谁也不想在最后关头功亏一篑。 查重率过高&#xff0c;无非以下两种原因。要么是作为“…

迅为RK3568开发板资料说明4750+页专属文档专为3568编写

iTOP-3568开发板采用瑞芯微RK3568处理器&#xff0c;内部集成了四核64位Cortex-A55处理器。主频高达2.0Ghz&#xff0c;RK809动态调频。集成了双核心架构GPU&#xff0c;ARM G52 2EE、支持OpenGLES1.1/2.0/3.2、OpenCL2.0、Vulkan1.1、内嵌高性能2D加速硬件。 内置独立NPU,算力…

VINS预积分与误差模型

文章目录 IMU的测量值误差模型IMU预积分真实模型IMU预积分估计模型误差模型普通增量积分中值积分法 参考文献 IMU的测量值误差模型 IMU的测量值误差模型&#xff1a; a ^ t a t R w t g w b a t n a t ω ^ t ω t b ω t n ω t \begin{array}{} {{{\hat a}_t} {a_t…

揭秘 IEEE/ACM Trans/CCF/SCI,谁才是科研界的王者?

会议之眼 快讯 在学术探索的浩瀚星海中&#xff0c;每一篇论文都像是一颗璀璨的星辰&#xff0c;而那些被顶级期刊或会议收录的论文&#xff0c;则无疑是最耀眼的几颗。 在众多评价标准中&#xff0c;IEEE/ACM Transactions、CCF推荐期刊和会议、SCI分区期刊&#xff0c;它们…

RVM(相关向量机)、CNN_RVM(卷积神经网络结合相关向量机)、RVM-Adaboost(相关向量机结合Adaboost)

当我们谈到RVM&#xff08;Relevance Vector Machine&#xff0c;相关向量机&#xff09;、CNN_RVM&#xff08;卷积神经网络结合相关向量机&#xff09;以及RVM-Adaboost&#xff08;相关向量机结合AdaBoost算法&#xff09;时&#xff0c;每种模型都有其独特的原理和结构。以…

【Delphi7】Access violation at address 0019F7C3. Write of address 0019F7C3.

这里写目录标题 问题基本情况问题描述1、启动Delphi 开发程序 时连续报如下错误2、打开“工程”菜单下的“选项”页面时时连续报如下错误 解决方案1、打开“高级系统设置”2、打开“性能选项”3、添加“数据执行保护”的程序4、选择“数据执行保护”的程序5、应用“数据执行保护…