Android Compose Column列表不自动刷新问题
目录
- 1. 背景
- 2. 解决方案
- 3. 原因
- 4. 结论
- 4.1 解决方案一
- 4.2 解决方案二
- 5.自己实现一个mutableStateOf()
1. 背景
我们都知道,Compose可以使用mutableStateOf和UI进行绑定,改变值之后,就可以改变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 = {
imageVwww.devze.comisible = !imageVisible //修改值,自动显示/隐藏UI
}) {
Text(text = "Show/Hide")
}
}
效果如下

但是如果是使用Column/Row/LazyColumn/LazyRow列表的时候,无论怎么更新数据,界面都不会刷新
val list = ArrayList<String>()
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<String>() }
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<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
interface State<out T> {
val value: T
}
查看mutableStateOf源码,可以发现,mutableStateOf()返回的是继承自MutableState的SnapshotMutableState对象,路径mutableStateOf()-> createSnapshot开发者_Python开发MutableState() -> 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 }
js }
}
private var next: StateStateRecord<T> = 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<String>()
for (value in stateList) {
tempList.add(value)
}
tempList.add("添加的值:${Random.nextInt()}")
stateList = tempList //赋值的时候会触发刷新UI
5.自己实现一个mutableStateOf()
我们也可以自己来实现一个mutableStateOf,伪代码如下
class Test {
interface State<out T> {
val value: T
}
interface MutableState<T> : State<T> {
override var value: T
/*operator fun component1(): T
operator fun component2(): (T) -> Unit*/
}
inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
inline operator fun <T> MutableState<T>.setValue(
thisObj: Any?,
property: KProperty<*>,
value: T
) {
this.value = value
}
interface SnapshotMutableState<T> : MutableState<T> {
val policy: SnapshotMutationPolicy<T>
}
interface SnapshotMutationPolicy<T> {
fun equivalent(a: T, b: T): Boolean
fun merge(previous: T, current: T, applied: T): T? = null
}
in编程客栈ternal open class SnapshotMutableStateImpl<T>(
val _value: T,
override val policy: SnapshotMutationPolicy<T>
) : /*StateObject, */SnapshotMutableState<T> {
private var next : T = 52 as T
@Suppress("UNCHECKED_CAST")
override var value: T
get() = next
/*get() {
Log.i(TAGs.TAG, "getValue:$field")
return "" as T
}*/
set(value) {
Log.i(TAGs.TAG, "setValue")
this.value = value
}
/*override fun component1(): T {
//TODO("Not yet implemented")
}
override fun component2(): (T) -> Unit {
//TODO("Not yet implemented")
}*/
}
internal class ParcelableSnapshotMutableState<T>(
value: T,
policy: SnapshotMutationPolicy<T>
) : SnapshotMutableStateImpl<T>(value, policy)/*, Parcelable*/ {
}
fun <T> mutableStateOf(
value: T,
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)
fun <T> structuralEqualityPolicy(): SnapshotMutationPolicy<T> =
StructuralEqualityPolicy as SnapshotMutationPolicy<T>
private object StructuralEqualityPolicy : SnapshotMutationPolicy<Any?> {
override fun equivalent(a: Any?, b: Any?) = a == b
override fun toString() = "StructuralEqualityPolicy"
}
fun <T> createSnapshotMutableState(
value: T,
policy: SnapshotMutationPolicy<T>
): SnapshotMutableState<T> = ParcelableSnapshotMutableState(value, policy)
fun main() {
var sizeUpdate by mutableStateOf(48)
Log.i(TAGs.TAG, "sizeUpdate:$sizeUpdate")
sizeUpdate = 64
Log.i(TAGs.TAG, "sizeUpdate>>$sizeUpdate")
}
}
到此这篇关于android Compose Column列表不自动刷新问题的文章就介绍到这了,更多相关Android Compose Column内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
加载中,请稍侯......
精彩评论