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 = {
        imageVisible = !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()返回的是继承自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,伪代码如下

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列表不自动刷新问题的文章就介绍到这了,更多相关Android Compose Column内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android开发Compose框架使用开篇

    目录 Compose的诞生 Compose好处 Compose 架构 @Composable的背后 智能重组真的那么智能吗 最后 Compose的诞生 在2019年的谷歌IO大会上,Compose作为Android新一代UI开发亮相,因为声明式开发越来越流行了,对标IOS开发SwiftUi,Compose的立项也为Android开发新加了声明式ui的开发选项,在2021年7月1.0正式版本的诞生,也意味着Compose即将进入生产环节,国际app巨头Twitter就首当其冲,在新页面上用上了Co

  • Android View与Compose互相调用实例探究

    目录 1. 前言 2. Android传统View调用Compose 2.1 新建传统View体系的Android项目 2.2 项目添加Compose配置 2.2.1 在android代码块添加 2.2.2 在dependencies中添加依赖 2.3 定义Compose函数 2.4 修改xml文件 2.5 关联Compose函数 2.6 运行项目 3. Compose中调用Android View 3.1 调用传统View的日历 3.1.1 使用AndroidView 3.1.2 显示效果如下

  • Android compose气泡升起和水滴下坠动画实现示例

    目录 摘要 知识点 解析 代码实现 动画绘制 结构 circle to bubble 摘要 今天用compose来构建一个气泡上升粘连动画和水滴下坠动画,Github源码点击这里 知识点 compose动画 贝塞尔曲线 缓动函数 compose canvas 解析 compose动画使用updateTransition,理由是: updateTransition可以管理多个动画作为子项,并且可以在多个状态间同时运行 这个动画恰巧需要维护两个状态,自定义状态数据Circle和Bubble 贝塞尔曲

  • Android开发Compose集成高德地图实例

    目录 正文 高德地图官网开发者建议 初始化MapView并添加到AndroidView里面 MapView增加一个管理地图生命周期的扩展 给MapView添加生命周期观察者 添加MapView的生命周期控制 正文 Compose中我们应该怎么使用地图呢?像之前我们在xml里面创建MapView,都是在Activity里面,管理MapView生命周期,和其他的监听器,Compose里面怎么搞? 下面我们以高德地图为例,在Compose中创建地图MapView,然后用AndroidView添加Map

  • Android Compose 属性动画使用探索详解

    目录 前言 使用探索 ObjectAnimator 使用探索 ValueAnimator 使用探索 Compose 函数中使用属性动画 实战 上传开始动画 上传进度动画 上传完成动画 最后 前言 Jetpack Compose(简称 Compose )是 Google 官方推出的基于 Kotlin 语言的 Android 新一代 UI 开发框架,其采用声明式的 UI 编程特性使得 Android 应用界面的编写和维护变得更加简单. 本专栏将详细介绍在使用 Compose 进行 UI 开发中如何实

  • Android 模仿iPhone列表数据View刷新动画详解

    因为我本人很喜欢在不同的页面之间跳转时加点好玩的动画,今天无意间看到一个动画效果感觉不错,几种效果图如下:既然好玩就写在博客中,直接说就是:该效果类似于iPhone中View的切换动画效果,今天就只介绍上面展示的效果. 废话不多说,先上效果,再看代码!! 效果一: 效果二: 效果三: 效果四:(犯错的效果): 效果五(回旋效果一): 效果六(回旋效果二): 效果看完了,就来看下上面效果实现的具体代码吧, 中间会把我自己试验的.犯的错误都以注释的形式写下来的, 大家使用的时候别出错就行了!先来看下

  • Android实现SwipeRefreshLayout首次进入自动刷新

    看到了Android版知乎实现了这种效果,就自己也实现了一下. 先来一张效果图 实现方式: 方法一: ①在onWindowFocusChanged()方法中,设置为刷新状态为true @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mSwipeRefreshLayout.setRefreshing(true); } ②在获取数据完成后设置刷新状

  • Android Jetpack Compose实现列表吸顶效果

    目录 stickyHeader 实体类 加载假数据 吸顶标题 二级条目 完整代码 效果图 安卓传统的 Recyclerview 打造悬浮头部StickyHeader的吸顶效果,十分麻烦,而在Compose中就简单多了 stickyHeader Compose设计的时候考虑得很周到,他们提供了stickyHeader 作用就是添加一个粘性标题项,即使在它后面滚动时也会保持固定.标头将保持固定,直到下一个标头取而代之. 参数key - 表示唯一的密钥键. 它不允许对列表出现使用相同的键.密钥的类型应

  • Android OkHttp实现全局过期token自动刷新示例

    问题 一次面试遇到的一个问题,其实也是实际开发中很容易遇到的问题,特此记录一下. 当请求某个接口的时候,我们会在请求的header中携带token消息,但是发现token失效,接口请求报错,怎么马上刷新token,然后重复请求方才那个接口呢?这个过程应该说对用户来说是无感的. 这个过程用流程图可以这样表示: 自动更新token流程 要实现上述需求的话,大家会如何实现呢? 首先讲一下Token和Cookie吧 - cookie cookie是保存在本地终端的数据.cookie由服务器生成,发送给浏

  • Android Compose实现底部按钮以及首页内容详细过程第1/2页

    目录 前言 Column.Row.ConstraintLayout布局先知 Column纵向排列布局 Row横向排列布局 ConstraintLayout 约束布局 Modifier的简单使用 底部导航栏的实现 首页内容的实现 Banner的实现 首页ViewModel 前言 compose作为Android现在主推的UI框架,各种文章铺天盖地的席卷而来,作为一名Android开发人员也是很有必要的学习一下了,这里就使用wanandroid的开放api来编写一个compose版本的玩安卓客户端,

  • Jetpack Compose实现列表和动画效果详解

    目录 创建一个列表消息卡片 可交互的动画效果 创建一个列表消息卡片 到目前为止,我们只有一个消息的卡片,看上去有点单调,所以让我们来改善它,让它拥有多条信息.我们需要创建一个能够显示多条消息的函数.对于这种情况,我们可以使用 Compose 的 LazyColumn 和 LazyRow.这些 Composable 只渲染屏幕上可见的元素,所以它们的设计对于长列表来说很有效果.同时,它们避免了 RecyclerView 与 XML 布局的复杂性. import androidx.compose.f

  • Android Compose衰减动画Animatable使用详解

    目录 前言 animateDecay splineBasedDecay rememberSplineBasedDecay exponentialDecay 实战 最后 前言 之前介绍了 Animatable 动画以及其 animateTo和 snapTo两个开启动画 api 的使用,实际上 Animatable 除了这两个 api 以外还有一个 animateDecay即本篇要介绍的衰减动画. 什么是衰减动画呢?就是动画速度由快到慢最后停止,最常见的应用场景就是惯性动画,比如滑动列表时手指松开后

  • Android编程实现列表侧滑删除的方法详解

    本文实例讲述了Android编程实现列表侧滑删除的方法.分享给大家供大家参考,具体如下: 前言:今天突然想起来了列表的滑动删除功能,一些下拉刷新的框架也会带这个侧滑删除的功能,比如一些listview的和recycleview的刷新框架都有这个功能,我今天写这个博客的目的是如何不依赖这些框架也是实现侧滑删除,如果自己已经使用的列表框架没有侧滑删除怎么给单独加入侧滑删除功能. 概括:我今天写的这个文章就是讲的是怎么单独给列表加入侧滑删除功能,不去为了侧滑删除而依赖一个列表框架,就是说如果需要的话可

  • Android实现短信验证码自动填写

    android应用经常会涉及到注册登录功能,而许多的注册登录或修改密码功能常常需要输入短信验证码,通常,用户收到短信需要最小化应用去查看短信再填入验证码,必然比较麻烦,因此有必要能够自动获得下发的短信验证码,方便了用户的操作,用户体验更好. 原理讲解: 主要就是实时获取短信信息.涉及到ContentObserver类的使用.使用ContentProvider来监听短信数据库的变化,在自定义的ContentObserver当中实现onChange的方法进行监听特定手机号的短信,然后进行信息截取在填

  • Android编程实现输入框动态自动提示功能

    本文实例讲述了Android编程实现输入框动态自动提示功能.分享给大家供大家参考,具体如下: 关于AutoCompleteTextView的使用,我想大家并不陌生,对其设定上Adapter后系统便能自己识别与匹配了.近期 一个项目中,需要做到匹配通迅录中的电话号码和联系人,由于通迅录中数据量大,所以把所有的数据在自己提示之前就查询出来并加入到 AutoCompleteTextView中是不现实的,所以我们可以使用cursor来动态加载AutoCompleteTextView的数据,从而 实现时时

随机推荐