在ViewModel中,我维护一个字符串列表。如果将执行< code>onAddTag或< code>onRemoveTag,则将刷新可变状态。这导致了重组。
问题是,如果我使用onRemveTag
删除一个元素(例如第三个),更新的列表会在我的“自定义可组合”(已调试)中到达,但渲染只会从列表中删除最后一项。
例:
在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")
}
}
最后,这里是一个工作示例。
@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将仅实例化一次,并且需要一种在重组时更新其内部状态的方法。
在主屏幕
可组合中,您正在用记住
来包装 val
赋值。它会导致列表在重新组合时不会更新,因此请删除那里的记忆
。然后,您还在初始化视图模型中
的 selectedTags
变量时使用了不太好的方法。您可以在其中直接使用委派,就像您在主要活动中使用的那样(或者看起来是可组合的)。在视图模型中,可以通过可变StateOf(listOf(listOf)将变量初始化为val selectedTags
此外,从 Compose 1.0.1 开始,声明可变列表的更好方法是使用预定义的
可变 StateListOf(...
),因此不需要使用委派('by'关键字),并且可以像常规列表对象一样使用它,而无需任何 .value
调用。
只要实施这些你就应该没事了
在我的应用程序中,我有标签的活动(让我们说10个标签)。每个选项卡页都包含和(此ListView中显示的数据是从我的服务器动态加载的)。我使用来显示这些页面。我不想将所有保存在内存中,所以我决定使用(我的适配器类扩展了这些类)。 > 假设选择了第三个选项卡。然后,当我转到第一个选项卡时,应该从头开始为该选项卡创建(这很好,这就是的工作方式),但不恢复该片段以前的状态(例如,不应该滚动到上次访问该时
问题内容: 在我的网站上,我正在创建发票功能。发票具有静态信息:公司信息和收件人信息。但是它也具有动态信息:小时数,描述,总金额等。客户可以使用上面的动态信息添加多行。 现在我的问题是,如何将其实现到数据库中? 目前,我有一个名为“发票”的表,其中的列将包含上面的所有信息。但是通过这种方式,行将具有不必要的信息,例如公司和收货人信息,而实际上每个发票只需要插入一次即可。 你们认为我将如何解决这个问
首选框架是Spring Web Service,但也欢迎其他解决方案。 问候,
问题内容: 我正在尝试克服Rails中动态表单字段的障碍- 这似乎是框架无法很好地处理的。我也在我的项目中使用jQuery。我已经安装了jRails,但是我更愿意在可能的情况下毫不干扰地编写AJAX代码。 我的表单相当复杂,嵌套的两个或三个级别并不罕见。我遇到的问题是生成正确的表单ID,因为它们是如此依赖于表单构建器上下文。我需要能够动态添加新字段或删除关系中的现有记录,而我完全不知所措。 到目前
目标是发送一个HTTP GET请求,其中包含表示枚举值的字符串列表问卷主题,然后使用这些参数选择正确主题的问题。我还添加了一个自定义转换器来将接收到的字符串转换为我的枚举。我的问题是当我在方法中调试时,“主题”总是为空。 这是我当前的RESTendpoint: 当我通过带有以下签名的方法传递单个主题时,我能够得到我的问题: 因此,将字符串转换为枚举似乎不是问题。 我尝试发送多个请求,但在endpo
我想生成jasper报表,用于打印在固定大小的文具上。我创建了html表,并将所有的值放在其中,根据我的要求,现在我想通过Java打印,所以我现在使用了jasper报告,因为我有列,是变化的时间。根据我的要求,我在网上搜索,我得到了动态的jasper是解决方案,但由于方法添加列需要列名和传递变量,这是从setter getter方法,但我的整个列是动态的,那么我如何生成setter getter方