Compose采取了声明式UI的开发范式。在这种范式中,UI的职责仅作为数据状态的反应。如果数据状态没有变化,则UI永远不会自行改变。如果把Composable的执行看作是一个函数运算,那么状态就是函数的参数,生成的布局就是函数的输出。
Stateless和Stateful
传统视图中通过获取组件对象句柄来更新组件状态,而Compose则通过重新执行Composable函数来更新页面(重组)。StatelessComposable只依赖参数的Composable;相对的,有些Composable内部持有或者访问了某些状态,称之为StatefulComposable。StatelessComposable的重组只能来自上层Composable的调用,而StatefulComposable的重组来自其以来的状态的变化。
//Statelesscomposable
@Composable
fun Hello(name:String){
Text(text = "Hello $name")
}
//StatefulComposable
@Preview
@Composable
fun CounterComponent() {
Column(
modifier = Modifier.padding(16.dp)
) {
//remember中计算得到的数据会自动缓存,当Composable重组再次执行到remember处会返回之前已缓存的数据,无须重新计算。mutableStateOf的调用一定要出现在remember中,不然每次重组都会创建新的状态。
var counter by remember {
mutableStateOf(0) }
Text( //1
"Click the buttons to adjust your value:",
Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
Text( //2
"$counter",
Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = typography.h3
)
Row {
Button(
onClick = {
counter-- },
Modifier.weight(1f)
) {
Text("-")
}
Spacer(Modifier.width(16.dp))
Button(
onClick = {
counter++ },
Modifier.weight(1f)
) {
Text("+")
}
}
}
}
在Compose中使用State<T>描述一个状态,泛型T是状态的具体类型。
interface State<out T> {
val value: T
}
State<T>是一个可观察对象。当Composabel对State的value进行读取时会与State建立订阅关系,当value发生变化时,作为监听者的Composable会自动重组刷新UI。
有时候Composable需要对State的value进行修改,比如在CounterComponent中单击按钮需要修改counter的值,所以counter可被修改,使用MutableState<T>来表示可修改状态,其包裹的数据是一个可修改的var类型。
@Stable
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
MutableState有三种用法:
-
创建MutableState
var counter:MutableState<Int> = mutableStateOf(0)
-
解构方式
val(counter,setCounter) = mutableStateOf(0)
此时的counter已经是一个Int类型的数据,后续使用的地方可以直接访问,无须再使用点操作符获取value,而需要更新counter的地方可以使用setCounter(xx)完成。
-
属性代理
使用by关键字直接获取Int类型counter。var counter by mutableStateOf(0)
by关键字的原理是对counter的读写会通过getValue和setValue这两个运算符的重写最终代理为对value的操作。
状态上提
状态上提的通常做法是将内部状态移除,通过参数传入需要在UI显示的状态,以及需要回调给调用方的事件,案例如下所示:
@Composable
fun CounterComponent(
counter:Int,//重组时调用方传入当前需要显示的计数
onIncrement:()->Unit,//向调用方回调单击加号的事件
onDecrement:()->Unit,//向调用方回调单击减号的事件
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text( //1
"Click the buttons to adjust your value:",
Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
Text( //2
"$counter",
Modifier.fillMaxWidth(),
tex