当前位置: 首页 > 知识库问答 >
问题:

Jetpack Compose:带有动态列表的“AndroidView”行

武琛
2023-03-14

在ViewModel中,我维护一个字符串列表。如果将执行< code>onAddTag或< code>onRemoveTag,则将刷新可变状态。这导致了重组。

问题是,如果我使用onRemveTag删除一个元素(例如第三个),更新的列表会在我的“自定义可组合”(已调试)中到达,但渲染只会从列表中删除最后一项。

例:

  • 初始列表: 1,2,3,4 -

LazyListScope.items的文档中,有一个关键参数,它说:

key——代表项目的稳定且唯一的键的工厂。不允许对列表中的多个项目使用同一个键。密钥的类型应该可以通过Android上的捆绑包保存。如果传递了null,列表中的位置将代表键。当您指定键时,滚动位置将根据键来保持,这意味着如果您在当前可见项目之前添加/删除项目,具有给定键的项目将保持为第一个可见项目。

也许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")
    }
}

这里你可以看到我使用了< code>EmojiTextView。如果我用一个基本的< code>Text composable来替换这个视图。它像预期的那样工作。使用< code>EmojiTextView时,项目将始终从右向左移除。

我能够将我的问题缩小到AndroidView集成:

kotlin prettyprint-override">@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")
    }
}

共有2个答案

白昊乾
2023-03-14

最后,这里是一个工作示例。

@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将仅实例化一次,并且需要一种在重组时更新其内部状态的方法。

胡沈义
2023-03-14

主屏幕可组合中,您正在用记住来包装 val 赋值。它会导致列表在重新组合时不会更新,因此请删除那里的记忆。然后,您还在初始化视图模型中selectedTags 变量时使用了不太好的方法。您可以在其中直接使用委派,就像您在主要活动中使用的那样(或者看起来是可组合的)。在视图模型中,可以通过可变StateOf(listOf(listOf)将变量初始化为val selectedTags

此外,从 Compose 1.0.1 开始,声明可变列表的更好方法是使用预定义的可变 StateListOf(...),因此不需要使用委派('by'关键字),并且可以像常规列表对象一样使用它,而无需任何 .value 调用。

只要实施这些你就应该没事了

 类似资料:
  • 在我的应用程序中,我有标签的活动(让我们说10个标签)。每个选项卡页都包含和(此ListView中显示的数据是从我的服务器动态加载的)。我使用来显示这些页面。我不想将所有保存在内存中,所以我决定使用(我的适配器类扩展了这些类)。 > 假设选择了第三个选项卡。然后,当我转到第一个选项卡时,应该从头开始为该选项卡创建(这很好,这就是的工作方式),但不恢复该片段以前的状态(例如,不应该滚动到上次访问该时

  • 问题内容: 在我的网站上,我正在创建发票功能。发票具有静态信息:公司信息和收件人信息。但是它也具有动态信息:小时数,描述,总金额等。客户可以使用上面的动态信息添加多行。 现在我的问题是,如何将其实现到数据库中? 目前,我有一个名为“发票”的表,其中的列将包含上面的所有信息。但是通过这种方式,行将具有不必要的信息,例如公司和收货人信息,而实际上每个发票只需要插入一次即可。 你们认为我将如何解决这个问

  • 首选框架是Spring Web Service,但也欢迎其他解决方案。 问候,

  • 问题内容: 我正在尝试克服Rails中动态表单字段的障碍- 这似乎是框架无法很好地处理的。我也在我的项目中使用jQuery。我已经安装了jRails,但是我更愿意在可能的情况下毫不干扰地编写AJAX代码。 我的表单相当复杂,嵌套的两个或三个级别并不罕见。我遇到的问题是生成正确的表单ID,因为它们是如此依赖于表单构建器上下文。我需要能够动态添加新字段或删除关系中的现有记录,而我完全不知所措。 到目前

  • 我想生成jasper报表,用于打印在固定大小的文具上。我创建了html表,并将所有的值放在其中,根据我的要求,现在我想通过Java打印,所以我现在使用了jasper报告,因为我有列,是变化的时间。根据我的要求,我在网上搜索,我得到了动态的jasper是解决方案,但由于方法添加列需要列名和传递变量,这是从setter getter方法,但我的整个列是动态的,那么我如何生成setter getter方

  • 目标是发送一个HTTP GET请求,其中包含表示枚举值的字符串列表问卷主题,然后使用这些参数选择正确主题的问题。我还添加了一个自定义转换器来将接收到的字符串转换为我的枚举。我的问题是当我在方法中调试时,“主题”总是为空。 这是我当前的RESTendpoint: 当我通过带有以下签名的方法传递单个主题时,我能够得到我的问题: 因此,将字符串转换为枚举似乎不是问题。 我尝试发送多个请求,但在endpo