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

这篇具有很好参考价值的文章主要介绍了Android Compose Column列表 数据更新列表不刷新的问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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 = {
        imageVisible = !imageVisible //修改值,自动显示/隐藏UI
    }) {
        Text(text = "Show/Hide")
    }
}

效果如下
Android Compose Column列表 数据更新列表不刷新的问题

但是如果是使用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()
        )
    }
}

可以看到,点击了按钮后,列表完全没有刷新
Android Compose Column列表 数据更新列表不刷新的问题
这是为什么了 ?

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()
        )
    }
}

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

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()返回的是继承自MutableStateSnapshotMutableState对象,路径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<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

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,伪代码如下文章来源地址https://www.toymoban.com/news/detail-414888.html

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
    }

    internal 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列表 数据更新列表不刷新的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Jetpack Compose 用计时器demo理解Compose UI 更新的关键-------状态管理(State)

    我们都知道了Compose使用了声明式的开发范式,在这样的范式中,UI的职责更加的单一,只会对数据状态的变化作出反应,如果数据状态没有发生变化,则UI就永远不会自行的改变。假如我们把Composable的执行看成是一个函数的运算的话,那么状态就是函数的参数,输出就是生成

    2024年02月09日
    浏览(53)
  • Android Jetpack Compose 用计数器demo理解Compose UI 更新的关键-------状态管理(State)

    我们都知道了Compose使用了声明式的开发范式,在这样的范式中,UI的职责更加的单一,只会对数据状态的变化作出反应,如果数据状态没有发生变化,则UI就永远不会自行的改变。假如我们把Composable的执行看成是一个函数的运算的话,那么状态就是函数的参数,输出就是生成

    2024年02月08日
    浏览(44)
  • 微信小程序--操作微信自带的返回按钮使上个页面刷新列表或刷新数据

    首先要了解什么是 页面栈 ------------ 框架以栈的形式维护了当前的所有页面。 当发生路由切换的时候,页面栈的表现如下: 路由方式 页面栈表现 初始化 新页面入栈 打开新页面 新页面入栈 页面重定向 当前页面出栈,新页面入栈 页面返回 页面不断出栈,直到目标返回页

    2024年02月13日
    浏览(48)
  • vue/uniapp - 返回上一页并onLoad/onShow刷新数据列表接口

    在uni中,返回页面是不会触发 onLoad 方法的; 如果我们只想在特定情况下返回上一页才需要刷新数据,那么用 onShow 的话,那刷新就太频繁了; 这时候,可以用 $emit 和 $on 去解决。 比如说,从详情页(detail.vue) 回到 列表页(list.vue): 进入的页面 给大佬暴赞👍👍👍👍👍👍

    2024年02月04日
    浏览(50)
  • Jetpack Compose布局控件Column、Row、Box 详解

    本篇文章介绍 Compose 的布局控件 Column 、 Row 、 Box ,在 Android XML 模式开发中,常用的布局控件有 LinearLayout 、 RelativeLayout 、 FrameLayout 以及 ConstraintLayout , Compose 没有延用之前的布局设计,而是提供了全新的布局控件,不仅实现了 XML 模式的相关功能并且更加灵活好用。 Compos

    2024年02月03日
    浏览(51)
  • Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题

    目录 Unity 问题 之 ScrollView ,LayoutGroup,ContentSizeFitter 一起使用时,动态变化时无法及时刷新更新适配界面的问题 一、简单介绍 二、问题现象 三、问题分析 四、问题解决思路 五、案例解决实现步骤 六、案例关键代码 Unity 在开发中,记录一些报错问题,以便后期遇到同样问题处

    2024年02月04日
    浏览(39)
  • vue数据更新但视图(DOM)不刷新解决办法

    第一种:强制更新dom 第二种:vue.$set(要操作的对象或数组, 对象key/索引, 修改后的值) 第三种: vue.$forceUpdate() 对此有什么疑问 或者 其它前端面临的问题都可以来欢迎评论  在下知道的定会帮你回答希望能够帮到你 (#^.^#)

    2024年02月11日
    浏览(38)
  • 猿创征文|vue vant-ui数据列表上拉加载更多,下拉刷新功能

    ⭐️⭐️⭐️   作者: 船长在船上 🚩🚩🚩   主页: 来访地址船长在船上的博客 🔨🔨🔨   简介: CSDN前端领域优质创作者,资深前端开发工程师,专注前端开发,在CSDN总结工作中遇到的问题或者问题解决方法以及对新技术的分享,欢迎咨询交流,共同学习。 🔔🔔🔔

    2023年04月12日
    浏览(43)
  • Pinia 刷新页面store数据更新页面没有变化,响应式失效

    当我在组件的 setup 中使用 useStore 来获取 store 的时候,Pinia.js 的 store 已经被更新了,但是组件渲染没变化。 当count我们在结构赋值的时候数据丢失了响应式 1.计算属性 2.使用storeToRefs

    2024年02月11日
    浏览(44)
  • Compose中使用paging3进行列表分页加载Room中的数据

    本文简要记录下流程,代码需要修改后才可以运行。 另外使用paging进行数据加载可以做到数据变动感知,只要数据库数据变动,页面就会自动刷新,无需额外操作 project/build.gradle app/build.gradle ViewModel UI androidx.paging.compose Paging 库概览

    2024年01月23日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包