Compose状态学习

 

状态从 ViewModel 向下流动到 activity,而事件从 activity 向上流动到 ViewModel。

状态观察使用:

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 : ViewModelComponentActivity.viewModels(
   noinline factoryProducer: (() -> Factory)null
): Lazy<VM> {
   val factoryPromise factoryProducer ?: {
       defaultViewModelProviderFactory
  }

   return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}


//可见内部也调用了observe(lifecycleOwner, observer)方法
@Composable
fun <R, T : RLiveData<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<TodoItemby 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 <TmutableStateListOf() 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 <Tremember(calculation: @DisallowComposableCalls () -> T): =
   currentComposer.cache(false, calculation)

fun <TmutableStateOf(
   value: T,
   policy: SnapshotMutationPolicy<TstructuralEqualityPolicy()
): MutableState<TcreateSnapshotMutableState(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) 参数对

提升状态时,有三条规则可帮助您弄清楚状态应去向何处

  1. 状态应至少提升到使用该状态(读取)的所有可组合项的最低共同父项

  2. 状态应至少提升到它可以发生变化(写入)的最高级别

  3. 如果两种状态发生变化以响应相同的事件,它们应一起提升

    您可以将状态提升到高于这些规则要求的级别,但欠提升状态会使遵循单向数据流变得困难或不可能。

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
  )
}
posted @ 2021-12-28 17:29  码虫垒起代码之城  阅读(609)  评论(0编辑  收藏  举报