Compose状态学习
状态观察使用:
Compose中的State封装了LiveData以及Observe从而达到同步的效果
/*一个使用例子
------------ViewModel----------------
封装一个list的LiveData
private var _todoItems = MutableLiveData(listOf<TodoItem>())
val todoItems: LiveData<List<TodoItem>> = _todoItems
---------------function----------------
//先获得ViewModel
private val todoViewModel by viewModels<TodoViewModel>()
//将ViewModel中的todoItems作为observe观察对象
val items: List<TodoItem> by todoViewModel.todoItems.observeAsState(listOf())
*/
//获得viewModel的函数
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
//可见内部也调用了observe(lifecycleOwner, observer)方法
@Composable
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
val lifecycleOwner = LocalLifecycleOwner.current
val state = remember { mutableStateOf(initial) }
DisposableEffect(this, lifecycleOwner) {
val observer = Observer<T> { state.value = it }
observe(lifecycleOwner, observer)
//在lifecycle Dispose的的时候取消观察
onDispose { removeObserver(observer) }
}
return state
}
//State是Compose中观察状态的接口
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
/*
val items: List<TodoItem> 声明了类型为 List<TodoItem> 的变量 items
todoViewModel.todoItems 是来自 ViewModel 的 LiveData<List<TodoItem>
.observeAsState 会观察 LiveData<T> 并将其转换为 State<T> 对象,让 Compose 可以响应值的变化
listOf() 是一个初始值,用于避免在初始化 LiveData 之前可能出现 null 结果。如果未传递,items 会是 List<TodoItem>?,可为 null。
by 是 Kotlin 中的属性委托语法,使我们可以自动将 State<List<TodoItem>> 从 observeAsState 解封为标准 List<TodoItem>
*/
val items: List<TodoItem> by todoViewModel.todoItem.observeAsState(listOf())
创建观察对象的三种方式:
val state = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
//那么上面那个例子的items可以重新写作:
/*
----------------ViewModel——--------------
var todoItemsNew = mutableStateListOf<TodoItem>()
private set
-----------------function-------------------
val item = ViewModel.todoItemsNew
*/
fun <T> mutableStateListOf() = SnapshotStateList<T>()
/*Params:
value - the initial value for the MutableState
policy - a policy to controls how changes are handled in mutable snapshots
即value为观察的值, setValue是操作函数 例如value是String 那么setValue会是 (String)->unit 用来改变value
*/
val (value, setValue) = remember { mutableStateOf(default) }
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
fun <T> mutableStateOf(
value: T,
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)
/*
在组合中创建 State<T>(或其他有状态对象)时,请务必对其执行 remember 操作。否则,它会在每次组合时进行重新初始化。
MutableState<T> 与 MutableLiveData<T> 类似,但已与 Compose 运行时集成。由于它是可观察对象,所以每次更新时,它都会告知 Compose,因此 Compose 可以重组读取对象的所有可组合项。
*/
//mutableStateOf 会创建 MutableState<T>,后者是 Compose 中内置的可观察状态容器。
interface MutableState<T> : State<T> {
override var value: T
}
remember:
可以记住在内存中,作为参数传入还是作为变量应该看使用场景
remember(todo.id) { randomTint() }
-
场景一:需要给他人设置参数
//调用者调用时可以根据参数实现别的操作,比如将iconAlpha设置为固定值,然后只对固定值操作
fun TodoRow(
todo: TodoItem,
modifier: Modifier = Modifier,
iconAlpha: Float = remember(todo.id) { randomTint() }
)
-
场景二:不需要给调用方控制
//保护参数
fun TodoRow(
todo: TodoItem,
modifier: Modifier = Modifier,
) {
iconAlpha: Float = remember(todo.id) { randomTint()}
}
状态提升
Compose中的状态提升: 将状态字段提升到外部,在lambda函数中传入改变 当应用于可组合项时,这通常意味着向可组合项引入两个参数。
-
value: T
- 要显示的当前值 -
onValueChange: (T) -> Unit
- 请求更改值的事件,其中T
是建议的新值
状态提升是一种将状态上移以使组件变为无状态的模式。
当应用于可组合项时,这通常意味着向可组合项引入两个参数。
value: T - 要显示的当前值 onValueChange: (T) -> Unit - 请求更改值的事件 如需提升状态,可以将可组合项的任何内置状态 T 重构为 (value: T, onValueChange: (T) -> Unit) 参数对。
提升状态时,有三条规则可帮助您弄清楚状态应去向何处
-
状态应至少提升到使用该状态(读取)的所有可组合项的最低共同父项
-
状态应至少提升到它可以发生变化(写入)的最高级别
-
如果两种状态发生变化以响应相同的事件,它们应一起提升
您可以将状态提升到高于这些规则要求的级别,但欠提升状态会使遵循单向数据流变得困难或不可能。
cmd + clt +m 可以新建一个函数 抽离为有状态和无状态两个部分
map
若一个变量为一个state值的一部分,会默认调用map
//val iconsVisible: LiveData<Boolean> = textLiveData.map { it.isNotBlank() }
val iconsVisible = text.isNotBlank()
监听软键盘
即要实现当按下确认键的时候实现一些逻辑:
如果要使用键盘处理操作,可以使用 TextField
提供的两个参数:
-
keyboardOptions
- 用于启用“完成”IME 操作的显示 -
keyboardActions
- 用于指定在响应特定 IME 操作时触发的操作 - 在本例中,当用户按下“完成”后,我们希望调用submit
并隐藏键盘。
为了控制软件键盘,我们需要使用 LocalSoftwareKeyboardController.current
。由于这是一个实验性 API,因此必须使用 @OptIn(ExperimentalComposeUiApi::class)
为该函数添加注解。
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun TodoInputText(
text: String,
onTextChange: (String) -> Unit,
modifier: Modifier = Modifier,
//当确认键按下
onImeAction: () -> Unit = {}
) {
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = text,
onValueChange = onTextChange,
colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent),
maxLines = 1,
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {
onImeAction()
keyboardController?.hide()
}),
modifier = modifier
)
}