Android Compose Column列表不自動刷新問題
1. 背景
我們都知道,Compose
可以使用mutableStateOf
和UI進行綁定,改變值之后,就可以改變UI。
var value by remember { mutableStateOf(0) } var imageVisible by remember { mutableStateOf(true) } Column { Text(text = "現(xiàn)在的值是:$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
列表的時候,無論怎么更新數(shù)據(jù),界面都不會刷新
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
源碼,可以發(fā)現(xiàn),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<T> = StateStateRecord(value)
這里就是重點,SnapshotMutableStateImpl
的value
屬性重寫了get()
和set()
方法
- 當
value
被讀的時候,不光把值返回,還會記錄一下在哪被讀的 - 當
value
被寫的時候,不止把這個值給改了,還會去查找在哪里被讀過,然后通知這些被讀過的地方,通知UI
進行刷新
4. 結論
因為我們操作String
、Int
等基礎類型的時候,都是通過get
、set()
來獲取、設置數(shù)據(jù)的,所以這操作會被SnapshotMutableStateImpl
記錄下來,而List
、Map
這種集合,我們是通過add
、remove
來更新數(shù)據(jù)的,所以不會觸發(fā)SnapshotMutableStateImpl
value
屬性的set
。
4.1 解決方案一
使用mutableStateListOf
替代mutableStateOf
,mutableStateListOf
內(nèi)部對add
、remove
方法也進行了重寫
4.2 解決方案二
新創(chuàng)建一個List
,然后賦值給原來的list
,這樣就會觸發(fā)set
了
var stateList by remember { mutableStateOf(list) } val tempList = ArrayList<String>() for (value in stateList) { tempList.add(value) } tempList.add("添加的值:${Random.nextInt()}") stateList = tempList //賦值的時候會觸發(fā)刷新UI
5.自己實現(xiàn)一個mutableStateOf()
我們也可以自己來實現(xiàn)一個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內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android應用第一次安裝成功點擊“打開”后Home鍵切出應用后再點擊桌面圖標返回導致應用重啟問題的解決方法
這篇文章主要介紹了Android應用第一次安裝成功點擊“打開”后Home鍵切出應用后再點擊桌面圖標返回導致應用重啟問題的解決方法,需要的朋友可以參考下2016-11-11android實現(xiàn)視頻的加密和解密(使用AES)
本篇文章主要介紹了android實現(xiàn)視頻的加密和解密(使用AES),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Android使用ViewPager實現(xiàn)滾動廣告
這篇文章主要為大家詳細介紹了Android使用ViewPager實現(xiàn)滾動廣告,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11詳解 Kotlin Reference Basic Types, String, Array and Imports
這篇文章主要介紹了詳解 Kotlin Reference Basic Types, String, Array and Imports的相關資料,需要的朋友可以參考下2017-06-06Android自定義View Material Design理念詳解
這篇文章主要為大家介紹了Android自定義View Material Design理念詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Android統(tǒng)一處理登錄后攜帶數(shù)據(jù)跳轉到目標頁面的方式
我們在開發(fā)的時候,一定會遇到頁面跳轉,下面這篇文章主要給大家介紹了關于Android統(tǒng)一處理登錄后攜帶數(shù)據(jù)跳轉到目標頁面的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-06-06android基于socket的局域網(wǎng)內(nèi)服務器與客戶端加密通信
本篇文章主要介紹了android基于socket的局域網(wǎng)內(nèi)服務器與客戶端加密通信,這里整理了詳細的代碼,有需要的小伙伴可以參考下。2017-04-04