Compose 状态管理

ops/2024/9/23 4:19:11/

文章目录

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

Compose__1">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.ppmy.cn/ops/36335.html

相关文章

sql server

SQL Server 是微软开发的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛用于企业级应用开发和数据管理。它遵循 SQL&#xff08;Structured Query Language&#xff09;标准&#xff0c;提供了数据存储、查询、更新和管理的功能。以下是 SQL Server 的一些…

如何实现源代码防泄漏?十种有效方法防止源代码泄漏

由于研发人员比普通办公人员要精通电脑&#xff0c;除了常见的网络&#xff0c;邮件&#xff0c;U盘&#xff0c;QQ等数据扩散方法外&#xff0c;还有很多对于研发人员来 说非常容易的方法&#xff0c;比如&#xff1a; —网线直连&#xff0c;即把网线从墙上插头拔下来&#…

图片编辑工具-Gimp

一、前言 GIMP&#xff08;GNU Image Manipulation Program&#xff09;是一款免费开源的图像编辑软件&#xff0c;具有功能强大和跨平台的特性。 GIMP作为一个图像编辑器&#xff0c;它提供了广泛的图像处理功能&#xff0c;包括但不限于照片修饰、图像合成以及创建艺术作品…

STM32——WWDG(窗口看门狗)

技术笔记&#xff01; 1.WWDG&#xff08;窗口看门狗&#xff09;简介 本质&#xff1a;能产生系统复位信号和提前唤醒中断的计数器。 特性&#xff1a; 递减的计数器&#xff1b; 当递减计数器值从 0x40减到0x3F时复位&#xff08;即T6位跳变到0&#xff09;&#xff1b; …

OpenAI API搭建的智能家居助手;私密大型语言模型(LLM)聊天机器人;视频和音频文件的自动化识别和翻译工具

✨ 1: GPT Home 基于Raspberry Pi和OpenAI API搭建的智能家居助手 GPT Home是一个基于Raspberry Pi和OpenAI API搭建的智能家居助手&#xff0c;功能上类似于Google Nest Hub或Amazon Alexa。通过详细的设置指南和配件列表&#xff0c;用户可以自行组装和配置这个设备&#x…

Java Solon v2.7.6 发布

Java Solon 是什么框架&#xff1f; Java “新的”应用开发框架。开放原子开源基金会&#xff0c;孵化项目。从零开始构建&#xff08;非 java-ee 架构&#xff09;&#xff0c;有灵活的接口规范与开放生态。 追求&#xff1a; 更快、更小、更简单提倡&#xff1a; 克制、简洁…

C++语法|using关键字

文章目录 1.类型别名Type Aliases2.命名空间别名Namespace Aliases4. 模板别名5.命名空间声明 (Namespace Alias)与typedef的区别 using 关键字在 C 中有两个最主要用途&#xff1a;类型别名和命名空间声明。 1.类型别名Type Aliases using 可以用来定义类型别名&#xff0c;这…

DigitalOcean 应用托管平台级更新:应用端到端运行时性能大幅改进

DigitalOcean 希望可以为企业提供所需的工具和基础设施&#xff0c;以帮助企业客户加速云端的开发&#xff0c;实现业务的指数级增长。为此 DigitalOcean 在 2020 年就推出了App Platform。 App Platform&#xff08;应用托管&#xff09; 是一个完全托管的 PaaS 解决方案&…