栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Java

Android Compose Column列表 数据更新列表不刷新的问题

Java 更新时间:发布时间: 百科书网 趣学号
1. 背景

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

var value by remember { mutableStateOf(0) }
var imageVisible by remember { mutableStateOf(true) }
Column {
    Text(text = "现在的值是:$value")
    Button(onClick = {
        value++ //修改值,自动改变UI
    }) {
        Text(text = "Add Value")
    }
    AnimatedVisibility(visible = imageVisible) {
        Image(
            painter = painterResource(id = R.mipmap.photot1),
            contentDescription = "",
            Modifier.width(260.dp)
        )
    }
    Button(onClick = {
        imageVisible = !imageVisible //修改值,自动显示/隐藏UI
    }) {
        Text(text = "Show/Hide")
    }
}

效果如下

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

val list = ArrayList()
for (i in 0..10) {
    list.add(i.toString())
}

var stateList by remember { mutableStateOf(list) }

Button(onClick = {
    stateList.add("添加的值:${Random.nextInt()}")
}, modifier = Modifier.fillMaxWidth()) {
    Text(text = "添加值")
}
Button(onClick = {
    stateList.removeAt(stateList.size - 1)
}, modifier = Modifier.fillMaxWidth()) {
    Text(text = "删除值")
}

LazyColumn {
    items(stateList.size) { index ->
        Text(
            text = "${stateList.get(index)}",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .height(24.dp)
                .fillMaxWidth()
        )
    }
}

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

这是为什么了 ?

2. 解决方案

当时很不解,为啥其他类型都是可以的,使用List就不行了呢 ?
查阅了好久,终于找到了解决方案
把mutableStateOf改用mutableStateListOf就可以了

var stateList = remember { mutableStateListOf() }
for (i in 0..10) {
    stateList.add(i.toString())
}

Button(onClick = {
    stateList.add("添加的值:${Random.nextInt()}")
}, modifier = Modifier.fillMaxWidth()) {
    Text(text = "添加值")
}
Button(onClick = {
    stateList.removeAt(stateList.size - 1)
}, modifier = Modifier.fillMaxWidth()) {
    Text(text = "删除值")
}
LazyColumn {
    items(stateList.size) { index ->
        Text(
            text = "${stateList.get(index)}",
            textAlign = TextAlign.Center,
            modifier = Modifier
                .height(24.dp)
                .fillMaxWidth()
        )
    }
}

3. 原因

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

3.1 mutableStateOf为什么可以更新UI

我们以mutableStateOf()这个为例

var value by mutableStateOf(0)

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

interface MutableState : State {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}

interface State {
    val value: T
}

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

override var value: T
    get() = next.readable(this).value
    set(value) = next.withCurrent {
        if (!policy.equivalent(it.value, value)) {
            next.overwritable(this, it) { this.value = value }
        }
    }

private var next: StateStateRecord = StateStateRecord(value)

这里就是重点,SnapshotMutableStateImpl的value属性重写了get()和set()方法

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

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

4.1 解决方案一

使用mutableStateListOf替代mutableStateOf,mutableStateListOf内部对add、remove方法也进行了重写

4.2 解决方案二

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

var stateList by remember { mutableStateOf(list) }

val tempList = ArrayList()
for (value in stateList) {
    tempList.add(value)
}
tempList.add("添加的值:${Random.nextInt()}")
stateList = tempList //赋值的时候会触发刷新UI
5.自己实现一个mutableStateOf()

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

class Test {
    interface State {
        val value: T
    }

    interface MutableState : State {
        override var value: T
        
    }

    inline operator fun  State.getValue(thisObj: Any?, property: KProperty<*>): T = value

    inline operator fun  MutableState.setValue(
        thisObj: Any?,
        property: KProperty<*>,
        value: T
    ) {
        this.value = value
    }

    interface SnapshotMutableState : MutableState {
        val policy: SnapshotMutationPolicy
    }

    interface SnapshotMutationPolicy {
        fun equivalent(a: T, b: T): Boolean

        fun merge(previous: T, current: T, applied: T): T? = null
    }

    internal open class SnapshotMutableStateImpl(
        val _value: T,
        override val policy: SnapshotMutationPolicy
    ) : SnapshotMutableState {
        private var next : T = 52 as T

        @Suppress("UNCHECKED_CAST")
        override var value: T
            get() = next
            
            set(value) {
                Log.i(TAGs.TAG, "setValue")
                this.value = value
            }

        

    }

    internal class ParcelableSnapshotMutableState(
        value: T,
        policy: SnapshotMutationPolicy
    ) : SnapshotMutableStateImpl(value, policy) {

    }

    fun  mutableStateOf(
        value: T,
        policy: SnapshotMutationPolicy = structuralEqualityPolicy()
    ): MutableState = createSnapshotMutableState(value, policy)

    fun  structuralEqualityPolicy(): SnapshotMutationPolicy =
        StructuralEqualityPolicy as SnapshotMutationPolicy

    private object StructuralEqualityPolicy : SnapshotMutationPolicy {
        override fun equivalent(a: Any?, b: Any?) = a == b

        override fun toString() = "StructuralEqualityPolicy"
    }

    fun  createSnapshotMutableState(
        value: T,
        policy: SnapshotMutationPolicy
    ): SnapshotMutableState = ParcelableSnapshotMutableState(value, policy)

    fun main() {
        var sizeUpdate by mutableStateOf(48)
        Log.i(TAGs.TAG, "sizeUpdate:$sizeUpdate")
        sizeUpdate = 64
        Log.i(TAGs.TAG, "sizeUpdate>>$sizeUpdate")
    }
}
转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/1076860.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号