Compose:MutableState 和 mutableStateOf

文章目录

  • [Compose 怎么更新界面?](#Compose 怎么更新界面?)
    • [Compose 怎么做到自动更新?](#Compose 怎么做到自动更新?)
    • [by 委托便捷写法](#by 委托便捷写法)
    • [用 State 什么时候用 by,什么时候用 =?](#用 State 什么时候用 by,什么时候用 =?)
  • 总结

Compose 怎么更新界面?

Compose 既然是声明式 UI,那肯定就不能像传统 UI 那样命令式的方式更新界面,声明式的界面更新就是,你把值改了界面就会自动更新,你的参数会被 Compose 自动监听

java 复制代码
class MainActivity : ComponentActivity() {
	// 注意这里是用 =
	// mutableStateOf 返回的 MutableState 对象
	val name = mutableStateOf("name")

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContent {
			Box(Modifier.safeContentPadding()) {
				// 因为 mutableStateOf 用的 =,赋值使用 name.value
				Text(name.value)
			}
		}
	
		lifecycleScope.launch {
			delay(3000)
			// 修改数值,就能通知到 Composable 刷新
			name.value = "vincent"
		}
	}
}   

上面是一个文字的 Composable,开启了一个协程 3s 后修改 name 的值,Compose 监听到 name 改变了,就会同步将 Text() 也更新。

需要注意的是,在 Compose 中要实现参数被监听,参数要使用 Compose 提供的 State 接口,把值装到它的里面才能被监听,真正被订阅的是这个 State 对象

java 复制代码
SnahshotState.kt

@Stable
public interface State<out T> {
	public val value: T
}

State 还有个子接口 MutableState:

java 复制代码
SnahshotState.kt

@Stable
public interface MutableState<T> : State<T> {
	override var value: T
	
	public operator fun component1(): T

	public operator fun component2(): (T) -> Unit
}

MutableState 在写法上使用 mutableStateOf 函数创建:

java 复制代码
val name = mutableStateOf("name")

Compose 怎么做到自动更新?

从最直观的角度解释,对于一段程序要执行更新,那么就是要重新执行这段代码,从上面的例子说就是 Text(name.value) 被重新执行了。

但是在运行时是不能指定执行某一段代码的,能做到的是找到用到这个变量的地方,拿到所在的函数类型的对象重新调用整个代码块,这也是 Compose 能做到自动更新的简单原理

java 复制代码
// 函数类型对象 content
Box(Modifier.safeContentPadding(), content = {
	Text(name.value)
})

by 委托便捷写法

每次使用 MutableState 都要 .value 才能使用很麻烦,Compose 为我们提供了简洁处理,帮我们去掉了这个操作,通过 by 将 setValue 和 getValue 都交由 mutableStateOf 委托处理。

java 复制代码
// name 实际类型此时是 String,mutableStateOf 通过 by 变成了代理类型
// val name = mutableStateOf("name")
var name by mutableStateOf("name") 
setContent {
	// Text(name.value)
    Text(name)
}

lifecycleScope.launch {
	delay(3000)
	// name.value = "vincent"
	name = "vincent"
}

Compose 引入了一个扩展函数 androidx.compose.runtime.setValue 和 androidx.compose.runtime.getValue,将 MutableState 类型转换为调用的数值类型,但目前仍需要手动导入这个扩展。

用 State 什么时候用 by,什么时候用 =?

我们先看下面的例子:

java 复制代码
class MainActivity : ComponentActivity() {
	var name by mutableStateOf("name")
	var name1 = name

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContent {
			Box(Modifier.safeContentPadding()) {
				// 注意这里是 name1
				Text(name1)
			}
		}
	
		lifecycleScope.launch {
			delay(3000)
			// 注意这里修改的是 name
			name = "vincent"
		}
	}
}   

运行上面的代码,会发现 3s 后代码不会自动更新,因为 Text(name1) 传的是 name1,修改的是 name,修改 name 时找不到使用的 Composable,所以不会更新。简单原理上面已经讲过,因为监听的对象是 State,name 使用的 by 声明,其实它已经是一个 String 类型,而不是 State。

要上面的代码生效,就要改为用 =:

java 复制代码
class MainActivity : ComponentActivity() {
	var name = mutableStateOf("name") // 改成了 =
	var name1 = name // name 赋值给 name1 此时才是 State 对象

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContent {
			Box(Modifier.safeContentPadding()) {
				// 注意这里是 name1
				Text(name1.value)
			}
		}
	
		lifecycleScope.launch {
			delay(3000)
			// 注意这里修改的是 name
			name.value = "vincent"
		}
	}
}   

by 的代理功能虽然在日常使用时很方便,但它的代理功能是不能通过赋值来传递的,也会丢失订阅监听,这种间接使用的场景就需要用 =

总结

  • Compose 的自动更新是通过 State 对象来实现的,使用 State 才能被订阅监听,在日常使用中是使用它的子接口 MutableState,通过 mutableStateOf 创建

  • 使用 by 关键字委托能更方便的使用 MutableState,但是在一些间接场景还是需要用 =

相关推荐
齊家治國平天下3 分钟前
AAOS 电源管理深度解析:休眠/唤醒/功耗优化
android·车载系统·aaos·aosp·电源管理·休眠唤醒·carpowermanager
Xzaveir29 分钟前
企业号码展示不可观测怎么办:状态机、拨测事件与异常回放
android·人工智能
2501_9160074738 分钟前
iOS和macOS应用程序性能分析和优化工具使用综合指南
android·macos·ios·小程序·uni-app·iphone·webview
黄林晴1 小时前
Nearby Connections 重大变更:不再自动开启 Wi-Fi / 蓝牙,适配指南
android
恋猫de小郭2 小时前
Flutter 全新真 3D 实现,用 flutter_scene 能开发一个「我的世界」
android·前端·flutter
HLC++15 小时前
Linux的进程间通信
android·linux·服务器
爱笑鱼18 小时前
Binder(二):AIDL 生成的 Proxy、Stub 和 Parcel 到底在做什么?
android
爱笑鱼19 小时前
Binder(一):一次方法调用,究竟怎样跨进程执行?
android
壮哥_icon19 小时前
【Android 系统开发】使用 BAT 脚本高效自动化管理 /system/priv-app/ 系统应用(安装与卸载)
android·运维·自动化
用户693717500138421 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端