经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android Compose Column列表不自动刷新问题
来源:jb51  时间:2023/2/1 9:35:30  对本文有异议

1. 背景

我们都知道,Compose可以使用mutableStateOf和UI进行绑定,改变值之后,就可以改变UI。

  1. var value by remember { mutableStateOf(0) }
  2. var imageVisible by remember { mutableStateOf(true) }
  3. Column {
  4. Text(text = "现在的值是:$value")
  5. Button(onClick = {
  6. value++ //修改值,自动改变UI
  7. }) {
  8. Text(text = "Add Value")
  9. }
  10. AnimatedVisibility(visible = imageVisible) {
  11. Image(
  12. painter = painterResource(id = R.mipmap.photot1),
  13. contentDescription = "",
  14. Modifier.width(260.dp)
  15. )
  16. }
  17. Button(onClick = {
  18. imageVisible = !imageVisible //修改值,自动显示/隐藏UI
  19. }) {
  20. Text(text = "Show/Hide")
  21. }
  22. }

效果如下

但是如果是使用Column/Row/LazyColumn/LazyRow列表的时候,无论怎么更新数据,界面都不会刷新

  1. val list = ArrayList<String>()
  2. for (i in 0..10) {
  3. list.add(i.toString())
  4. }
  5. var stateList by remember { mutableStateOf(list) }
  6. Button(onClick = {
  7. stateList.add("添加的值:${Random.nextInt()}")
  8. }, modifier = Modifier.fillMaxWidth()) {
  9. Text(text = "添加值")
  10. }
  11. Button(onClick = {
  12. stateList.removeAt(stateList.size - 1)
  13. }, modifier = Modifier.fillMaxWidth()) {
  14. Text(text = "删除值")
  15. }
  16. LazyColumn {
  17. items(stateList.size) { index ->
  18. Text(
  19. text = "${stateList.get(index)}",
  20. textAlign = TextAlign.Center,
  21. modifier = Modifier
  22. .height(24.dp)
  23. .fillMaxWidth()
  24. )
  25. }
  26. }

可以看到,点击了按钮后,列表完全没有刷新

这是为什么了 ?

2. 解决方案

当时很不解,为啥其他类型都是可以的,使用List就不行了呢 ?

查阅了好久,终于找到了解决方案

mutableStateOf改用mutableStateListOf就可以了

  1. var stateList = remember { mutableStateListOf<String>() }
  2. for (i in 0..10) {
  3. stateList.add(i.toString())
  4. }
  5. Button(onClick = {
  6. stateList.add("添加的值:${Random.nextInt()}")
  7. }, modifier = Modifier.fillMaxWidth()) {
  8. Text(text = "添加值")
  9. }
  10. Button(onClick = {
  11. stateList.removeAt(stateList.size - 1)
  12. }, modifier = Modifier.fillMaxWidth()) {
  13. Text(text = "删除值")
  14. }
  15. LazyColumn {
  16. items(stateList.size) { index ->
  17. Text(
  18. text = "${stateList.get(index)}",
  19. textAlign = TextAlign.Center,
  20. modifier = Modifier
  21. .height(24.dp)
  22. .fillMaxWidth()
  23. )
  24. }
  25. }

3. 原因

解决方案很简单,但是这是为什么呢 ?

3.1 mutableStateOf为什么可以更新UI

我们以mutableStateOf()这个为例

  1. var value by mutableStateOf(0)

首先,我们要明白,mutableStateOf()返回的是一个MutableState对象,MutableState中有一个var value: T属性

  1. interface MutableState<T> : State<T> {
  2. override var value: T
  3. operator fun component1(): T
  4. operator fun component2(): (T) -> Unit
  5. }
  6. interface State<out T> {
  7. val value: T
  8. }

查看mutableStateOf源码,可以发现,mutableStateOf()返回的是继承自MutableStateSnapshotMutableState对象,路径mutableStateOf()-> createSnapshotMutableState() -> ParcelableSnapshotMutableState-> SnapshotMutableStateImpl,可以看到有这样一段代码

  1. override var value: T
  2. get() = next.readable(this).value
  3. set(value) = next.withCurrent {
  4. if (!policy.equivalent(it.value, value)) {
  5. next.overwritable(this, it) { this.value = value }
  6. }
  7. }
  8. private var next: StateStateRecord<T> = StateStateRecord(value)

这里就是重点,SnapshotMutableStateImplvalue属性重写了get()set()方法

  • value被读的时候,不光把值返回,还会记录一下在哪被读的
  • value被写的时候,不止把这个值给改了,还会去查找在哪里被读过,然后通知这些被读过的地方,通知UI进行刷新

4. 结论

因为我们操作StringInt等基础类型的时候,都是通过getset()来获取、设置数据的,所以这操作会被SnapshotMutableStateImpl记录下来,而ListMap这种集合,我们是通过addremove来更新数据的,所以不会触发SnapshotMutableStateImpl value属性的set

4.1 解决方案一

使用mutableStateListOf替代mutableStateOfmutableStateListOf内部对addremove方法也进行了重写

4.2 解决方案二

新创建一个List,然后赋值给原来的list,这样就会触发set

  1. var stateList by remember { mutableStateOf(list) }
  2. val tempList = ArrayList<String>()
  3. for (value in stateList) {
  4. tempList.add(value)
  5. }
  6. tempList.add("添加的值:${Random.nextInt()}")
  7. stateList = tempList //赋值的时候会触发刷新UI

5.自己实现一个mutableStateOf()

我们也可以自己来实现一个mutableStateOf,伪代码如下

  1. class Test {
  2. interface State<out T> {
  3. val value: T
  4. }
  5. interface MutableState<T> : State<T> {
  6. override var value: T
  7. /*operator fun component1(): T
  8. operator fun component2(): (T) -> Unit*/
  9. }
  10. inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
  11. inline operator fun <T> MutableState<T>.setValue(
  12. thisObj: Any?,
  13. property: KProperty<*>,
  14. value: T
  15. ) {
  16. this.value = value
  17. }
  18. interface SnapshotMutableState<T> : MutableState<T> {
  19. val policy: SnapshotMutationPolicy<T>
  20. }
  21. interface SnapshotMutationPolicy<T> {
  22. fun equivalent(a: T, b: T): Boolean
  23. fun merge(previous: T, current: T, applied: T): T? = null
  24. }
  25. internal open class SnapshotMutableStateImpl<T>(
  26. val _value: T,
  27. override val policy: SnapshotMutationPolicy<T>
  28. ) : /*StateObject, */SnapshotMutableState<T> {
  29. private var next : T = 52 as T
  30. @Suppress("UNCHECKED_CAST")
  31. override var value: T
  32. get() = next
  33. /*get() {
  34. Log.i(TAGs.TAG, "getValue:$field")
  35. return "" as T
  36. }*/
  37. set(value) {
  38. Log.i(TAGs.TAG, "setValue")
  39. this.value = value
  40. }
  41. /*override fun component1(): T {
  42. //TODO("Not yet implemented")
  43. }
  44. override fun component2(): (T) -> Unit {
  45. //TODO("Not yet implemented")
  46. }*/
  47. }
  48. internal class ParcelableSnapshotMutableState<T>(
  49. value: T,
  50. policy: SnapshotMutationPolicy<T>
  51. ) : SnapshotMutableStateImpl<T>(value, policy)/*, Parcelable*/ {
  52. }
  53. fun <T> mutableStateOf(
  54. value: T,
  55. policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
  56. ): MutableState<T> = createSnapshotMutableState(value, policy)
  57. fun <T> structuralEqualityPolicy(): SnapshotMutationPolicy<T> =
  58. StructuralEqualityPolicy as SnapshotMutationPolicy<T>
  59. private object StructuralEqualityPolicy : SnapshotMutationPolicy<Any?> {
  60. override fun equivalent(a: Any?, b: Any?) = a == b
  61. override fun toString() = "StructuralEqualityPolicy"
  62. }
  63. fun <T> createSnapshotMutableState(
  64. value: T,
  65. policy: SnapshotMutationPolicy<T>
  66. ): SnapshotMutableState<T> = ParcelableSnapshotMutableState(value, policy)
  67. fun main() {
  68. var sizeUpdate by mutableStateOf(48)
  69. Log.i(TAGs.TAG, "sizeUpdate:$sizeUpdate")
  70. sizeUpdate = 64
  71. Log.i(TAGs.TAG, "sizeUpdate>>$sizeUpdate")
  72. }
  73. }

到此这篇关于Android Compose Column列表不自动刷新问题的文章就介绍到这了,更多相关Android Compose Column内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号