android - 添加新项目时,Jetpack Compose 模型列表变得混乱
问题描述
我对 Jetpack compose 显示包含一系列ModelList
项目的模型时遇到问题。添加新项目时,UI 元素的顺序会变得不正确。
这是一个非常简单CounterModel
的包含 a ModelList
of ItemModel
s:
@Model
data class CounterModel(
var counter: Int = 0,
var name: String = "",
val items: ModelList<ItemModel> = ModelList()
)
@Model
data class ItemModel(
var name: String
)
屏幕显示两个卡片行,每个ItemModel
:RowA
和RowB
。当我创建使用以下内容初始化的此屏幕时CounterModel
:
val model = CounterModel()
model.name="hi"
model.items.add(ItemModel("Item 1"))
model.items.add(ItemModel("Item 2"))
CounterModelScreen(model)
...它按预期显示如下:
项目 1 A 行
项目 1 B 行
项目 2 A 行
项目 2 B 行
当我单击“添加”按钮以插入新的ItemModel
时,我只是希望看到
项目 3 A 行
项目 3 B 行
在底部。但是相反,顺序是混乱的,我看到两个 rowAs 然后是两个 rowB:
项目 1 A 行
项目 1 B 行
项目 2 A 行
项目 3 A 行
项目 3 B 行
项目 2 B 行
我真的不明白这怎么可能。UI 代码非常简单:循环遍历items
并发出RowA
和RowB
为每个:
for (i in counterModel.items.indices) {
RowA(counterModel, i)
RowB(counterModel, i)
}
使用 Android Studio 4.0C6
这是完整的代码:
@Composable
fun CounterModelScreen(counterModel: CounterModel) {
Column {
TopAppBar(title = {
Text(
text = "Counter Model"
)
})
CounterHeader(counterModel)
for (i in counterModel.items.indices) {
RowA(counterModel, i)
RowB(counterModel, i)
}
Button(
text = "Add",
onClick = {
counterModel.items.add(ItemModel("Item " + (counterModel.items.size + 1)))
})
}
}
@Composable
fun CounterHeader(counterModel: CounterModel) {
Text(text = counterModel.name)
}
@Composable
fun RowA(counterModel: CounterModel, index: Int) {
Padding(padding = 8.dp) {
Card(color = Color.White, shape = RoundedCornerShape(4.dp)) {
Column(crossAxisSize = LayoutSize.Expand) {
Text(
text = counterModel.items[index].name
)
Text(text = "Row A")
}
}
}
}
@Composable
fun RowB(counterModel: CounterModel, index: Int) {
Padding(padding = 8.dp) {
Card(color = Color.Gray, shape = RoundedCornerShape(4.dp)) {
Column(crossAxisSize = LayoutSize.Expand) {
Text(
text = counterModel.items[index].name
)
Text(text = "Row B")
}
}
}
}
解决方案
我已经使用compose-1.0.0-alpha07 对其进行了测试,并进行了一些更改以使代码适应更改后的 API。一切都完美无缺,所以我的猜测是,旧版本的 compose 中出现了一些问题,因为代码看起来正确并且在具有上述更改的更新版本中工作。
我还修改了您的代码以使用文档中推荐的状态,并添加了一个 ViewModel 来帮助您将视图与数据管理分离:
视图模型
class CounterModelViewModel : ViewModel() {
private val myBaseModel = CounterModel().apply {
name = "hi"
items.add(ItemModel("Item 1"))
items.add(ItemModel("Item 2"))
}
private val _modelLiveData = MutableLiveData(myBaseModel)
val modelLiveData: LiveData<CounterModel> = _modelLiveData
fun addNewItem() {
val oldCounterModel = modelLiveData.value ?: CounterModel()
// Items is casted to a new MutableList because the new state won't be notified if the new
// counter model content is the same one as the old one. You can also change any other
// properties instead like the name or the counter
val newItemsList = oldCounterModel.items.toMutableList()
newItemsList.add(ItemModel("Item " + (newItemsList.size + 1)))
// Pass a new instance of CounterModel to the LiveData
val newCounterModel = oldCounterModel.copy(items = newItemsList)
_modelLiveData.value = newCounterModel
}
}
可组合视图更新:
@Composable
fun CounterModelScreen(counterModel: CounterModel, onAddNewItem: () -> Unit) {
ScrollableColumn {
TopAppBar(title = {
Text(
text = "Counter Model"
)
})
CounterHeader(counterModel)
counterModel.items.forEachIndexed { index, item ->
RowA(counterModel, index)
RowB(counterModel, index)
}
Button(
onClick = onAddNewItem
) {
Text(text = "Add")
}
}
}
@Composable
fun CounterHeader(counterModel: CounterModel) {
Text(text = counterModel.name)
}
@Composable
fun RowA(counterModel: CounterModel, index: Int) {
Card(
backgroundColor = Color.White,
shape = RoundedCornerShape(4.dp),
modifier = Modifier.padding(8.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = counterModel.items[index].name
)
Text(text = "Row A")
}
}
}
@Composable
fun RowB(counterModel: CounterModel, index: Int) {
Card(
backgroundColor = Color.Gray,
shape = RoundedCornerShape(4.dp),
modifier = Modifier.padding(8.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = counterModel.items[index].name
)
Text(text = "Row B")
}
}
}
前面的代码是从另一个包含 ViewModel 实例的可组合函数调用的,但是您可以将其更改为具有上述 ViewModel 实例的活动或片段,这取决于您的偏好。
@Composable
fun MyCustomScreen(viewModel: CounterModelViewModel = viewModel()) {
val modelState: CounterModel by viewModel.modelLiveData.observeAsState(CounterModel())
CounterModelScreen(
counterModel = modelState,
onAddNewItem = {
viewModel.addNewItem()
}
)
}
推荐阅读
- git - 致命:pathspec 'editme.html' 不匹配任何文件
- css - 在 Bootstrap 4 导航选项卡上切换样式
- vba - 如果 Excel 单元格为空,则在文本文档中打印空格
- javascript - 使用hammer.js和jQuery向下和向上滑动元素以删除/删除元素?
- php - REST 服务使用 PHP 7
- c# - 对于此代码段,PLINQ 比实际 Linq 慢
- google-cloud-pubsub - 发布子主题与项目错误无关
- sql-server - SQL Server 嵌入式 CASE 语句
- gradle - 在以下任何来源中均未找到插件 [id: 'org.jetbrains.kotlin.jvm', version: '1.2.71']
- c++ - 类中主要 cpp 错误中的变量