三九集团如何进行网站建设,wordpress页面调用,企业管理培训课程方案,建设网站比较好的公司吗前言 引用一段官方描述#xff0c;如下 由于 Compose 是声明式工具集#xff0c;因此更新它的唯一方法是通过新参数调用同一可组合项。这些参数是界面状态的表现形式。每当状态更新时#xff0c;都会发生重组。因此#xff0c;TextField 不会像在基于 XML 的命令式视图中那…前言 引用一段官方描述如下 由于 Compose 是声明式工具集因此更新它的唯一方法是通过新参数调用同一可组合项。这些参数是界面状态的表现形式。每当状态更新时都会发生重组。因此TextField 不会像在基于 XML 的命令式视图中那样自动更新。可组合项必须明确获知新状态才能相应地进行更新。 google想表达的是compose不会像xml布局一样可以简单的在代码里主动调用方法比如setText()setImageResource()等等就能去刷新UI内容而是需要通过状态管理通知UI的内容需要更新。 而Compose的状态管理更符合MVVM思想的虽然Jetpack很早之前就已经推出了ViewModel、LiveData、MutableLiveData、DataBinding(这个最有毒开创了XML写逻辑的先河)作为状态管理。但是因为XML与Activity的定位原因都让现在的Android编程很难说是MVVM模式只能说是接近。 其中尴尬的原因是: 1.XML既是View层实现编写但是又无法更新控制View层。并且XML的样式就已经表明它不适合编写逻辑控制View。 2.Activity既是View层控制器又不实现View的代码编写。 他们本应该合二为一但是却分开了。导致MVCMVPMVVM思想都无法完全契合Android平台使很多新人在Android平台学习使用这3种思想时会经常陷入困惑。 状态管理涉及到类与方法
remember保存数据并且在UI更新时会提供保存的值。但是Activity页面退出后会丢失保存的值rememberSaveable保存数据并且将值写入到bundle中然后重新构建Activity的时候从bundle读数据。这表示Activity退出后也不会丢失值。mutableStateOf 一个可变并且被Compose时刻观察的状态存储作用就是让Compose可以获知数据已经改变UI上的内容需要重新绘制。mutableStateListOfmutableStateOf只能观察单个类型数据的变化无法观察到集合数据的变化。所以有了mutableStateListOf方法参数带vararg关键字所以它也可以是多个List组成的数组mutableStateMapOf同上只不过是以哈希的形式方法参数带vararg关键字所以它也可以是数组derivedStateOf定义的对象状态依赖其他的对象状态时需要使用derivedStateOf当依赖对象状态发生改变自己也可以跟着改变。
看完上面的可以明白remember是用于临时保存数据的而MutableState是用于通知与传递数据变化的。 remember与mutableStateOf 的使用例子一个快速了解的Demo
实现一个按键点击自增数值并且显示的Demo一般情况下mutableStateOf 与 remember都是配合使用的但是他们不是绑定关系都可以单独使用。直接使用mutableStateOf 与 remember组合使用的区别是什么请看博客后面的”为什么mutableStateOf不能直接写到方法内部的例子“ 但是建议你先保留疑问按顺序看下去。
下面代码里展示了3种创建方式但是这3种方式都是不同的语法糖结果是一样的。
代码
class DeploymentActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyButton()}}Preview(name 按键自增计数)Composablefun MyButton() {Column() {/*使用by需要引用import androidx.compose.runtime.getValueimport androidx.compose.runtime.setValue*/var count1 by remember { mutableStateOf(0) }Button(onClick { count1 }) {Text(text 按键A $count1)}var count2 remember { mutableStateOf(0) }Button(onClick { count2.value }) {Text(text 按键B ${count2.value})}var (count3, setValue) remember { mutableStateOf(0) }Button(onClick { setValue.invoke(count31) }) {Text(text 按键C $count3)}}}
}
效果动图 mutableStateListOf的使用例子
mutableStateListOf 是用在集合数据的情况下它能在集合数据变动的情况下触发重组因为如果使用mutableStateOf将会无法观察到集合数据的变动从而不触发重组。
private var mImageList mutableStateListOfString()Composableprivate fun collectContentList() {LazyVerticalGrid(columns GridCells.Adaptive(minSize 256.dp),verticalArrangement Arrangement.spacedBy(20.dp),horizontalArrangement Arrangement.spacedBy(20.dp),contentPadding PaddingValues(top 20.dp, start 20.dp, end 20.dp)) {items(mImageList.size) { index -AsyncImage(model mImageList[index],contentDescription null,contentScale ContentScale.Crop,modifier Modifier.width(256.dp).height(128.dp).pointerInput(Unit) {detectTapGestures(onTap {DrawFromActivity.jumpCarryFileImage(context requireContext(),mImageList[index])},onLongPress {mCurrentDeleteImagePath mImageList[index]mIsShowDeleteDialog.value true})}.clip(RoundedCornerShape(10.dp)))}}} mutableStateMapOf的使用例子 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyMapList()}}Composablefun MyMapList() {val dataMap remember {mutableStateMapOf(key1 to value1,key2 to value2,key3 to value3,key4 to value4)}LazyColumn {items(dataMap.size){ index-val itemKey dataMap.keys.toMutableList()[index]val itemValue dataMap[itemKey]itemValue?.let { Text(text it) }}}} derivedStateOf的使用例子 derivedStateOf的使用场景是某个数据需要依靠其他状态管理的计算或者派生的情况。
代码例子如下
我们需要计数并且计数的结果派生一新的需求判断是奇数还是偶数。 PreviewComposablefun MyText() {val count remember { mutableStateOf(0) }//是否是奇数val isOddNumber remember {derivedStateOf {count.value % 2 ! 0}}Text(text 计数 ${count.value} 是否是奇数 ${isOddNumber.value},color Color.White,modifier Modifier.clickable {count.value})}
结果 remember的带参使用例子 remember不带参的使用例子已经在上面说明过了不在重复举例。现在说说remember的带参使用例子。 remember的代码块只会在第一次创建的时候执行一次后续就不会在执行了。如果我们有需求希望在Compose方法重组的时候remember的代码块在执行一次怎么办 那就需要使用remember带参的情况只要改变key就会让remember在compose重组的时候重新执行一次代码块。
举一个反面参考不带参的代码例子 Composablefun MyText() {//这个count是用来触发整个方法重组的val count remember { mutableStateOf(0) }//不添加keyval randomNum remember() {Log.e(zh, remember被重新执行代码块了)(0..99).random()}Text(text 按键A ${count.value}, modifier Modifier.clickable {count.valueLog.e(zh, randomNum ${randomNum})})} 点击Text后重组的结果可以看到随机数没有变化固定在51并且在remember代码块里的log日志也没有打印。 带参的例子
请注意因为remember是在Compose内部的所以想让带参remember重新执行代码块就需要让Compose发生一次重组所以下面的count是用来触发重组的。 var key 0Composablefun MyText() {//这个count是用来触发整个方法重组的val count remember { mutableStateOf(0) }//添加keyval randomNum remember(key) {Log.e(zh, remember被重新执行代码块了)(0..99).random()}Text(text 按键A ${count.value}, modifier Modifier.clickable {keycount.valueLog.e(zh, key ${key})Log.e(zh, randomNum ${randomNum})})}
点击Text后重组的结果可以因为key的改变randomNum的remember也被触发重新执行了代码块。从而更新了随机数的值。 rememberSaveable的使用例子
保存数据并且将值写入到bundle中然后重新构建Activity的时候从bundle读数据。这表示Activity退出后也不会丢失值。 代码 Composablefun MyText() {val count rememberSaveable {mutableStateOf(0)}Text(text 计数 ${count.value} ,color Color.Black,modifier Modifier.clickable {count.value})} 理解MutableState重组UI组件范围
mutableState的重组UI组件范围是在它读取与写入的范围里的。为了验证这个说法请看下面的代码例子
多个Composable方法组合下的重组UI范围例子1
下面的代码中在Column被点击后增加了count的数值但是并不会引起任何的UI重组。因为三个Text都没有引用count。 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {val count remember { mutableStateOf(1) }Column(modifier Modifier.clickable { count.value }) {Log.e(zh, Column触发重组)Text1()Text2()Text3()}}}Composablefun Text1() {Log.e(zh, Text1触发重组)Text(text 测试)}Composablefun Text2() {Log.e(zh, Text2触发重组)Text(text 测试)}Composablefun Text3() {Log.e(zh, Text3触发重组)Text(text 测试)}
多个Composable方法组合下的重组UI范围例子2
在下面的代码中Text1引用了count数据所以在点击Columu增加了count数值后重组范围只在自定义的Text1方法里在外部的Column也没有触发重组。 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {val count remember { mutableStateOf(1) }Column(modifier Modifier.clickable { count.value }) {Log.e(zh, Column触发重组)Text1(count)Text2(count)Text3(count)}}}Composablefun Text1(count: MutableStateInt) {Log.e(zh, Text1触发重组 count ${count.value} count内存地址 ${count})Text(text 测试 ${count.value})}Composablefun Text2(count: MutableStateInt) {Log.e(zh, Text2触发重组)Text(text 测试)}Composablefun Text3(count: MutableStateInt) {Log.e(zh, Text3触发重组)Text(text 测试)}
Composable方法内部的重组范围例子 在Text被点击后方法内部的所有组件都被重组了。但是有特例并不是所有情况下整个方法内部都会触发重组在调用了Button、Surface、CompositionLocalProvider情况下重组范围只会被限制在这些组件的内部其实Button、Surface内部含CompositionLocalProvider导致的重组只会限制在他们的范围内 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyButton()}}Composablefun MyButton() {val count remember { mutableStateOf(1) }Log.e(zh, 触发重组1)Column {Log.e(zh, 触发重组2)Column {Log.e(zh, 触发重组3)Text(text 数值 ${count.value}, modifier Modifier.width(100.dp).height(100.dp).clickable { count.value })}}}
Composable方法内部CompositionLocalProvider的重组范围例子
Button、Surface内部含CompositionLocalProvider所以一起举例。在下面的代码中点击任何一个组件增加Count数值后Column下的任何log都不会触发了因为重组范围被限定在CompositionLocalProvider。 override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyButton()}}OptIn(ExperimentalMaterialApi::class)Composablefun MyButton() {val count remember { mutableStateOf(1) }Log.e(zh, 触发重组1)Column {Log.e(zh, 触发重组2)Column {Log.e(zh, 触发重组3)Button(onClick { count.value }) {Log.e(zh, Button触发重组)Text(text Button ${count.value})}CompositionLocalProvider(){Log.e(zh, CompositionLocalProvider触发重组)Text(text CompositionLocalProvider ${count.value}, modifier Modifier.width(100.dp).height(100.dp).clickable { count.value })}Surface(onClick { count.value },modifier Modifier.width(100.dp).height(100.dp)) {Log.e(zh, Surface触发重组)Text(text Surface ${count.value})}}}}
为什么mutableStateOf不能直接写到方法内部的例子
在上面的例子里所有创建mutableStateOf的外部都套了一个remember。 那么肯定有人会疑问为什么要增加remember 不直接在方法内部创建mutableStateOf呢 其实这个问题的关键是理解组件的重组。因为组件方法的每一次重组都会导致 mutableStateOf 被重新创建一次。remember的文字意思是记住所以remember的作用就是将mutableStateOf或者其他实体数据引用到保存到每个Compose的SlotTable中不受其重组的影响。 在下面的代码中我们故意错误的在组件方法里直接创建mutableStateOf。看看在Text点击后让Count自增后重组后会引起什么问题 SuppressLint(UnrememberedMutableState) //在内部调用mutableStateOf会出现警告Composablefun MyButton() {val count mutableStateOf(1)Log.e(zh, count地址 ${count})Column {//因为Button含有CompositionLocalProvider不会导致外部也触发重组所以这里用Text替代Text(text 按键A ${count.value}, modifier Modifier.clickable { count.value })Log.e(zh, count ${count.value})}}
结果就是每次组件方法的重组也把MutableState重新创建了导致数值不会自增并且内存地址每次都是新的。 但是mutableStateOf可以写在外部下面代码中mCount1是保存在Activity这个类的全局变量中而count2是保存在Composable创建的Compose的SlotTable中但是二者在使用上没有什么特别大的区别。 val mCount1 mutableStateOf(1)Composablefun MyText() {val count2 remember { mutableStateOf(1) }Column {Text(text mCount1 ${mCount1})Text(text count2 ${count2})}} MutableState通知UI重组机制
这里用下面的图片可以简单了解一下... .MutableState的机制相当复杂想要深入了解特别烧脑。因为代码追踪并不好用你得用到debug调试才能找到他们的观察者消息的发送与接收。个人认为只要了解SnapshotMutableStateImpl简单的理解State与快照Snapshot的机制即可。 remember的原理
下面用贴源码方式展示remember的流程看看remember将数据缓存到哪里去了。 源码一 /*** 记住高阶函数calculation执行后产生的值。重组将总是返回产生的值*/
Composable
inline fun T remember(calculation: DisallowComposableCalls () - T): T currentComposer.cache(false, calculation)
源码二
/*** A Compose compiler plugin API. DO NOT call directly.* 缓存记录一个组合的组合数据中的值。编译器插件使用它来生成更有效的调用以便在确定这些操作是安全的时候进行记录。*/
ComposeCompilerApi
inline fun T Composer.cache(invalid: Boolean, block: DisallowComposableCalls () - T): T {Suppress(UNCHECKED_CAST)return rememberedValue().let {if (invalid || it Composer.Empty) {val value block()updateRememberedValue(value)value} else it} as T
}
源码三 override fun updateRememberedValue(value: Any?) updateValue(value)
源码四 /*** 将SlotTable的值更新为[value]的当前值。** param value the value to schedule to be written to the slot table.*/PublishedApiOptIn(InternalComposeApi::class)internal fun updateValue(value: Any?) {if (inserting) {//插入新的值writer.update(value)if (value is RememberObserver) {record { _, _, rememberManager - rememberManager.remembering(value) }abandonSet.add(value)}} else {//更新已经存在的值val groupSlotIndex reader.groupSlotIndex - 1if (value is RememberObserver) {abandonSet.add(value)}recordSlotTableOperation(forParent true) { _, slots, rememberManager -if (value is RememberObserver) {rememberManager.remembering(value)}//这里的set方法可以将新值保存到SlotTable里并且将旧的值返回when (val previous slots.set(groupSlotIndex, value)) {is RememberObserver -//观察者记录管理类将以前的注册的RememberObserver观察者移除rememberManager.forgetting(previous)//重组范围实施类is RecomposeScopeImpl - {val composition previous.compositionif (composition ! null) {//释放之前的值previous.release()//设置当前composition失效范围composition.pendingInvalidScopes true}}}}}} remember存在的意义是什么 在文章上面的 “为什么mutableStateOf不能直接写到方法内部的例子” 中已经讲解了大部分。这边在重复啰嗦一下意义就是给每个Compose保存一份需要缓存的数据使其不受到Compose重组的影响。这种设计是因为移动平台的应用有切换前后台需求从而有页面生命周期的概念。需要Compose缓存一份数据用于前后台切换后的数据恢复展示。