[英]Jetpack Compose: Row of "AndroidView" with dynamic list
在 ViewModel 中,我維護一個字符串列表。 如果將執行onAddTag
或onRemoveTag
,則 mutableState 將被刷新。 這會導致重組。
問題是,如果我使用onRemoveTag
刪除一個元素(例如第三個),更新后的列表會到達我的“自定義可組合項”(已調試),但渲染只會從列表中刪除最后一項。
例子:
在LazyListScope.items
的文檔中,有一個key
參數說:
key - 代表項目的穩定且唯一的密鑰工廠。 不允許對列表中的多個項目使用相同的鍵。 密鑰的類型應該可以通過 Android 上的 Bundle 保存。如果 null 被傳遞,列表中的 position 將代表密鑰。 當您指定鍵時,滾動 position 將根據鍵進行維護,這意味着如果您在當前可見項目之前添加/刪除項目,則具有給定鍵的項目將保留為第一個可見項目。
也許Row
做了類似的事情,它不能正確地決定哪個元素需要被刪除,哪個只保留位置信息。 但是如何解決呢?
我不想在這里使用LazyRow
,因為我只有幾個項目要呈現。 除此之外,我想了解這個問題::)
視圖模型
class MyViewModel : ViewModel() {
var selectedTags = mutableStateOf(listOf<String>())
fun onRemoveTag(tag: String) {
selectedTags.value = selectedTags.value.toMutableList().apply { remove(tag) }
}
fun onAddTag(tag: String) {
selectedTags.value = selectedTags.value.toMutableList().apply { add(tag) }
}
}
主屏幕
fun MainScreen(viewModel: MyViewModel) {
val selectedTags by remember { viewModel.selectedTags }
TagRow(tags = selectedTags)
}
自定義組合
@Composable
fun TagRow(
tags: List<String>,
modifier: Modifier = Modifier
) {
Row(modifier = modifier) {
tags.forEach {
Text(text = it)
}
}
}
希望有人知道如何解決這個問題!
問候,克里斯
根據@Philip 的反饋,我准備了一個獨立的例子:
@Composable
fun StackOverflowPreview() {
val tags = remember { mutableStateListOf("1", "2", "3") }
Row {
tags.forEach {
AndroidView(factory = { context ->
EmojiTextView(context).apply {
textAlignment = View.TEXT_ALIGNMENT_CENTER
setTextColor(android.graphics.Color.BLACK)
layoutParams =
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
text = it
}
})
}
}
LaunchedEffect(key1 = tags) {
delay(2000)
tags.remove("1")
delay(2000)
tags.remove("2")
delay(2000)
tags.remove("3")
}
}
在這里你可以看到我使用EmojiTextView
。 如果我用基本Text
可組合項替換該視圖。 它按預期工作。 使用EmojiTextView
,項目將始終從右向左移除。
我能夠將我的問題縮小到 AndroidView 集成:
@Preview
@Composable
fun StackOverflowPreview() {
val tags = remember { mutableStateListOf("1", "2", "3") }
Row {
tags.forEach { tag ->
// does NOT work
// AndroidView(factory = { TextView(it).apply { text = tag } })
// works
AndroidView(factory = { TextView(it) }, update = { v -> v.text = tag })
}
}
LaunchedEffect(key1 = tags) {
delay(2000)
tags.remove("1")
delay(2000)
tags.remove("2")
delay(2000)
tags.remove("3")
}
}
在MainScreen
可組合項中,您用remember
包裝了val
賦值。 它會導致列表不會在重組時更新,因此remember
那里的刪除。 然后,您在viewmodel
中的selectedTags
變量的初始化中也使用了不太好的方法。 您可以直接在其中使用委托,就像您在主要活動中使用的那樣(或者看起來是可組合的)。 在您的視圖模型中,您可以val selectedTags by mutableStateOf(listOf<String>())
,然后就不需要在任何地方使用.value
前綴。 Rest 確保仍然會觸發重組。 您只需要使用mutableStateOf()
進行初始化。
此外,從 Compose 1.0.1 開始,聲明可變列表的更好方法是使用預定義的mutableStateListOf(...)
,因此您不需要使用委托(“by”關鍵字),並且可以使用它就像一個常規列表 object,沒有任何.value
調用。
只要實施這些,你應該沒問題
在這里完成這個線程是一個工作示例。
@Preview
@Composable
fun StackOverflowPreview() {
val tags = remember { mutableStateListOf("1", "2", "3") }
Row {
tags.forEach { tag ->
AndroidView(factory = { TextView(it) }, update = { v -> v.text = tag })
}
}
LaunchedEffect(key1 = tags) {
delay(2000)
tags.remove("1")
delay(2000)
tags.remove("2")
delay(2000)
tags.remove("3")
}
}
正如@Philip 在評論中提到的,AndroidView 將只實例化一次,並且需要一種方法來更新其內部 state 重組。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.