Compose 编程思想
总结
- navigation+ViewModel+hilt
- 所有和耗时相关的操作放到ViewModel里
- 在@Composable的方法里调用ViewModel的方法来更新mutableStateOf相关的持久化数据的字段,此字段在回传给@Composable进行UI的更新
- box组件相当于FramLayout, 可以设置叠加效果和圆角等属性
- 使用modifier的lambda方法一些列方法,它会跳过重组并在组合阶段之外执行动画
这会跳过重组并在组合阶段之外执行动画
声明性编程
- Compose是使用声明式来更新UI而不是命令式
- 就是数据和UI的单项数据流,数据改变UI自动改变。 命令式编程很容易数据改变,忘记对某处UI的修改,或是多个地方同时修改UI而造成异常的状态
- Compose会智能的选择只对数据改变的地方进行UI的重新绘制,而不是刷新整个界面
简单的组合函数
- 使用Compose来写UI就是定义各种带有@Composable的方法(方法名应大写好区分),他不需要返回值,可以传递各种数据参数和点击的回调
- 定义这些参数应该保持幂等性,就是传入参数相关得到的结果是一致,从而保证高效、安全的更新界面。
- 这个函数不应该修改全局变量、修改任何共享资源
- 将UI界面纯粹描述一个函数的结果,不依赖于外部状态或修改外部的状态
动态生成内容
- 因为你使用的是kotlin来编写UI,所以你可以动态写逻辑判断是否显示、如何显示 拥有了kotlin底层语言逻辑的全部灵活性
@Composable
fun Greeting(names: List<String>) {
for (name in names) {
Text("Hello $name")
}
}
重组
- 在命令式编程中。当需要更新UI,需要手动的找View重新设置,而Compose中只要数据发生改变他会自动调用方法进行更新
每次点击button,会执行count++ 都重新调用ClickCounter方法并会打印println log ClickCounter:
var count by remember { mutableStateOf(0) }
ClickCounter(count) {
count++
}
@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
println("ClickCounter: $clicks")
Button(onClick = onClick) {
Text("I've been clicked $clicks times")
}
}
- 所以这些函数可能会很频繁执行;不按顺序的执行;或是并发执行
- 所以下面的代码 你不能通过StartScreen()改变全局变量,然后在MiddleScreen()去获取改变变量的值,因为他们的执行顺序不一定保证一致,Compose来控制优先绘制哪些组件,应该让每个函数都保存独立。
@Composable
fun ButtonRow() {
MyFancyNavigation {
StartScreen()
MiddleScreen()
EndScreen()
}
}
- 所有的附带效应的状态,应该通过onClick在UI线程来触发
- 重组操作是永远保持最新的状态,如果连续2次重组进行界面更新,他可能会只保留最新的一次,所以如果你的函数带附带效果,可能会只执行了最后一次,导致非预期的结果。应保持函数独立。
状态管理
- 如果想在配置更改(语言、屏幕旋转)依然可以保留数据,使用rememberSaveable来持久化数据 var name by rememberSaveable { mutableStateOf("") }
虽然是在方法里创建了ViewModel,但他是复用的。当前界面任何一个方法创建都是同一个对象。
@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
println("ClickCounter: $clicks")
val vm = viewModel(MainViewModel::class.java)
Button(onClick = onClick) {
Text("I've been ${vm.hashCode()} clicked $clicks times")
}
}
有状态与无状态
- 这个 HelloContent是有状态的方法,他内部会自动修改Name这个持久化数据字段,调用方不需要处理状态的管理。但这个方法不便于复用
@Composable
fun HelloContent() {
Column(modifier = Modifier.padding(16.dp)) {
var name by remember { mutableStateOf("") }
if (name.isNotEmpty()) {
Text(
text = "Hello, $name!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
}
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Name") }
)
}
}
- 通过把方法可变和不可变部分分离出来。把状态数据部分抽取出来让调用方来处理,把方法变为无状态形式,这样提高可复用性。
@Composable
fun HelloScreen() {
var name by rememberSaveable { mutableStateOf("") }
HelloContent(name = name, onNameChange = { name = it })
}
@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Hello, $name",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") })
}
}