簡體   English   中英

Jetpack Compose:帶有動態列表的“AndroidView”行

[英]Jetpack Compose: Row of "AndroidView" with dynamic list

在 ViewModel 中,我維護一個字符串列表。 如果將執行onAddTagonRemoveTag ,則 mutableState 將被刷新。 這會導致重組。

問題是,如果我使用onRemoveTag刪除一個元素(例如第三個),更新后的列表會到達我的“自定義可組合項”(已調試),但渲染只會從列表中刪除最后一項。

例子:

  • 初始列表:1,2,3,4 -> onRemove(2) -> 更新列表:1,3,4 -> 渲染:1,2,3
  • 初始列表:1,2,3,4 -> onRemove(1) -> 更新列表:2,3,4 -> 渲染:1,2,3

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 ,項目將始終從右向左移除。

編輯 2

我能夠將我的問題縮小到 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM