Android Compose Column列表不自動(dòng)刷新問(wèn)題
1. 背景
我們都知道,Compose可以使用mutableStateOf和UI進(jìn)行綁定,改變值之后,就可以改變UI。
var value by remember { mutableStateOf(0) }
var imageVisible by remember { mutableStateOf(true) }
Column {
Text(text = "現(xiàn)在的值是:$value")
Button(onClick = {
value++ //修改值,自動(dòng)改變UI
}) {
Text(text = "Add Value")
}
AnimatedVisibility(visible = imageVisible) {
Image(
painter = painterResource(id = R.mipmap.photot1),
contentDescription = "",
Modifier.width(260.dp)
)
}
Button(onClick = {
imageVisible = !imageVisible //修改值,自動(dòng)顯示/隱藏UI
}) {
Text(text = "Show/Hide")
}
}
效果如下

但是如果是使用Column/Row/LazyColumn/LazyRow列表的時(shí)候,無(wú)論怎么更新數(shù)據(jù),界面都不會(huì)刷新
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()
)
}
}
可以看到,點(diǎn)擊了按鈕后,列表完全沒(méi)有刷新

這是為什么了 ?
2. 解決方案
當(dāng)時(shí)很不解,為啥其他類(lèi)型都是可以的,使用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. 原因
解決方案很簡(jiǎn)單,但是這是為什么呢 ?
3.1 mutableStateOf為什么可以更新UI
我們以mutableStateOf()這個(gè)為例
var value by mutableStateOf(0)
首先,我們要明白,mutableStateOf()返回的是一個(gè)MutableState對(duì)象,MutableState中有一個(gè)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對(duì)象,路徑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)
這里就是重點(diǎn),SnapshotMutableStateImpl的value屬性重寫(xiě)了get()和set()方法
- 當(dāng)
value被讀的時(shí)候,不光把值返回,還會(huì)記錄一下在哪被讀的 - 當(dāng)
value被寫(xiě)的時(shí)候,不止把這個(gè)值給改了,還會(huì)去查找在哪里被讀過(guò),然后通知這些被讀過(guò)的地方,通知UI進(jìn)行刷新
4. 結(jié)論
因?yàn)槲覀儾僮?code>String、Int等基礎(chǔ)類(lèi)型的時(shí)候,都是通過(guò)get、set()來(lái)獲取、設(shè)置數(shù)據(jù)的,所以這操作會(huì)被SnapshotMutableStateImpl記錄下來(lái),而List、Map這種集合,我們是通過(guò)add、remove來(lái)更新數(shù)據(jù)的,所以不會(huì)觸發(fā)SnapshotMutableStateImpl value屬性的set。
4.1 解決方案一
使用mutableStateListOf替代mutableStateOf,mutableStateListOf內(nèi)部對(duì)add、remove方法也進(jìn)行了重寫(xiě)
4.2 解決方案二
新創(chuàng)建一個(gè)List,然后賦值給原來(lái)的list,這樣就會(huì)觸發(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 //賦值的時(shí)候會(huì)觸發(fā)刷新UI
5.自己實(shí)現(xiàn)一個(gè)mutableStateOf()
我們也可以自己來(lái)實(shí)現(xiàn)一個(gè)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")
}
}
到此這篇關(guān)于Android Compose Column列表不自動(dòng)刷新問(wèn)題的文章就介紹到這了,更多相關(guān)Android Compose Column內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
快速解決設(shè)置Android 23.0以上版本對(duì)SD卡的讀寫(xiě)權(quán)限無(wú)效的問(wèn)題
今天小編就為大家分享一篇快速解決設(shè)置Android 23.0以上版本對(duì)SD卡的讀寫(xiě)權(quán)限無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
android將圖片轉(zhuǎn)換存到數(shù)據(jù)庫(kù)再?gòu)臄?shù)據(jù)庫(kù)讀取轉(zhuǎn)換成圖片實(shí)現(xiàn)代碼
有時(shí)候我們想把圖片存入到數(shù)據(jù)庫(kù)中,盡管這不是一種明智的選擇,但有時(shí)候還是不得以會(huì)用到,下面說(shuō)說(shuō)將圖片轉(zhuǎn)換成byte[]數(shù)組存入到數(shù)據(jù)庫(kù)中去,并從數(shù)據(jù)庫(kù)中取出來(lái)轉(zhuǎn)換成圖像顯示出來(lái)2013-11-11
Android實(shí)現(xiàn)彩信附件的添加與刪除功能
這篇文章主要介紹了Android實(shí)現(xiàn)彩信附件的添加與刪除功能,涉及Android針對(duì)常見(jiàn)多媒體文件的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
Android 手機(jī)瀏覽器調(diào)試使用Chrome進(jìn)行調(diào)試實(shí)例詳解
這篇文章主要介紹了Android 手機(jī)瀏覽器調(diào)試使用Chrome進(jìn)行調(diào)試實(shí)例詳解的相關(guān)資料,這里提供了實(shí)例,需要的朋友可以參考下2016-12-12
Android基礎(chǔ)控件RadioGroup使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android基礎(chǔ)控件RadioGroup的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android實(shí)現(xiàn)仿通訊錄側(cè)邊欄滑動(dòng)SiderBar效果代碼
這篇文章主要介紹了Android實(shí)現(xiàn)仿通訊錄側(cè)邊欄滑動(dòng)SiderBar效果代碼,實(shí)例分析了通訊錄側(cè)邊欄滑動(dòng)效果的實(shí)現(xiàn)技巧,并附帶完整實(shí)例代碼供讀者下載參考,需要的朋友可以參考下2015-10-10
Android中的TimePickerView(時(shí)間選擇器)的用法詳解
這篇文章主要介紹了Android中的TimePickerView時(shí)間選擇器的用法,這是一個(gè)第三方從底部彈出來(lái)的日期選擇器,文中結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
Android webview實(shí)現(xiàn)拍照的方法
這篇文章主要介紹了Android webview實(shí)現(xiàn)拍照的方法的相關(guān)資料,希望通過(guò)本文能幫助到大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10

