gpt4 book ai didi

android - 模型中列表的值已更新,但未反射(reflect)在可组合函数上

转载 作者:行者123 更新时间:2023-11-30 04:56:04 24 4
gpt4 key购买 nike

我正在创建使用 jetpack compose 和 mvvm 的演示项目,我创建了包含用户列表的模型类..这些用户显示在列表中,顶部有一个按钮,单击时将新用户添加到列表中...
当用户单击按钮时,lambda 会更新有关它的 Activity , Activity 会调用 viewmodel 将数据添加到列表并使用 livedata 更新回 Activity ,现在模型收到新数据后,它不会更新有关它的可组合函数,因此不会更新 ui名单没有更新。。
这是代码

@Model
data class UsersState(var users: ArrayList<UserModel> = ArrayList())

Activity
class MainActivity : AppCompatActivity() {
private val usersState: UsersState = UsersState()
private val usersListViewModel: UsersListViewModel = UsersListViewModel()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

usersListViewModel.getUsers().observe(this, Observer {
usersState.users.addAll(it)
})
usersListViewModel.addUsers()
setContent {
UsersListUi.addList(
usersState,
onAddClick = { usersListViewModel.addNewUser() },
onRemoveClick = { usersListViewModel.removeFirstUser() })
}
}

}

View 模型
class UsersListViewModel {

private val usersList: MutableLiveData<ArrayList<UserModel>> by lazy {
MutableLiveData<ArrayList<UserModel>>()
}
private val users: ArrayList<UserModel> = ArrayList()
fun addUsers() {
users.add(UserModel("jon", "doe", "android developer"))
users.add(UserModel("john", "doe", "flutter developer"))
users.add(UserModel("jonn", "dove", "ios developer"))
usersList.value = users
}

fun getUsers(): MutableLiveData<ArrayList<UserModel>> {
return usersList
}

fun addNewUser() {
users.add(UserModel("jony", "dove", "ruby developer"))
usersList.value = users
}

fun removeFirstUser() {
if (!users.isNullOrEmpty()) {
users.removeAt(0)
usersList.value = users
}
}
}

可组合函数
@Composable
fun addList(state: UsersState, onAddClick: () -> Unit, onRemoveClick: () -> Unit) {
MaterialTheme {
FlexColumn {
inflexible {
// Item height will be equal content height
TopAppBar( // App Bar with title
title = { Text("Users") }
)
FlexRow() {
expanded(flex = 1f) {
Button(
text = "add",
onClick = { onAddClick.invoke() },
style = OutlinedButtonStyle()
)

}
expanded(flex = 1f) {
Button(
text = "sub",
onClick = { onRemoveClick.invoke() },
style = OutlinedButtonStyle()
)
}
}
VerticalScroller {
Column {
state.users.forEach {
Column {
Row {
Text(text = it.userName)
WidthSpacer(width = 2.dp)
Text(text = it.userSurName)
}
Text(text = it.userJob)

}
Divider(color = Color.Black, height = 1.dp)

}
}
}
}

}

}
}


整个源代码可用 here

我不确定我是否做错了什么,还是因为 jetpack compose 仍处于开发者预览版中,所以将不胜感激。
谢谢你

最佳答案

哎呀!

来自 Android Devrel 的 Sean 在这里。这不更新的主要原因是 UserState.users 中的 ArrayList 是不可观察的——它只是一个常规的 ArrayList,所以改变它不会更新 compose。

模型使模型类的所有属性都可观察

看起来这可能有效,因为 UserState 被注释为 @Model ,这使得 Compose 可以自动观察到事情。但是,可观察性仅适用于一层深度。这是一个永远不会触发重组的示例:

class ModelState(var username: String, var email: String)

@Model
class MyImmutableModel(val state: ModelState())

由于 state 变量是不可变的 ( val ),当您更改 emailusername 时,Compose 永远不会触发重组。这是因为 @Model 仅适用于被注释的类的属性。在这个例子中, state 在 Compose 中是可观察的,但 usernameemail 只是常规字符串。

修复选项 #0:您不需要 @Model

在这种情况下,您已经从 LiveData 获得了一个 getUsers() – 您可以在 compose 中观察到。我们还没有在开发版本中发布 Compose 观察,但是在我们发布观察方法之前,可以使用效果编写一个观察方法。请记住删除 onDispose {} 中的观察者。

如果您使用任何其他可观察类型,如 FlowFlowable 等,这也是正确的。您可以将它们直接传递给 @Composable 函数并观察它们的效果,而无需引入中间 @Model 类。

修复选项 #1:在 @Model 中使用不可变类型

许多开发人员更喜欢 UI 状态的不可变数据类型(像 MVI 这样的模式鼓励这样做)。您可以更新您的示例以使用不可变列表,然后为了更改列表,您必须分配给 Compose 可观察的 users 属性。
@Model
class UsersState(var users: List<UserModel> = listOf())

然后当你想更新它时,你必须分配 users 变量:
val usersState = UsersState()

// ...
fun addUsers(newUsers: List<UserModel>) {
usersState.users = usersState.users + newUsers
// performance note: note this allocates a new list every time on the main thread
// which may be OK if this is rarely called and lists are small
// it's too expensive for large lists or if this is called often
}

每当将新的 List<UserModel 分配给 users 时,这将始终触发重新组合,并且由于在分配列表后无法编辑列表,因此 UI 将始终显示当前状态。

在这种情况下,由于数据结构是 List,因此您将不可变类型的性能连接起来可能是 Not Acceptable 。然而,如果你持有一个不可变的 data class 这个选项是一个很好的选择,所以我把它包括在内是为了完整性。

修复选项 #2:使用 ModelList

对于这个用例,Compose 有一个特殊的可观察列表类型。您可以使用 ArrayList 代替,并且对列表的任何更改都可以通过 compose 观察到。
@Model
class UsersState(val users: ModelList<UserModel> = ModelList())

如果您使用 ModelList,您在 Activity 中编写的其余代码将正常工作,并且 Compose 将能够直接观察对 users 的更改。

相关:嵌套@Model 类

值得注意的是,您可以嵌套 @Model 类,这就是 ModelList 版本的工作原理。回到开头的示例,如果您将两个类都注释为@Model,那么所有属性都将在 Compose 中可见。
@Model
class ModelState(var username: String, var email: String)

@Model
class MyModel(var state: ModelState())

注意:此版本将 @Model 添加到 ModelState ,并且还允许在 MyModel 中重新分配状态

由于 @Model 使被 compose 注释的类的所有属性都可观察,因此 stateusernameemail 都将是可观察的。

TL;DR 选择哪个选项

在此代码中完全避免 @Model(选项 #0)将避免仅为 Compose 引入重复的模型层。由于您已经在 ViewModel 中保存状态并通过 LiveData 公开它,因此您可以直接传递 LiveData 以在那里编写和观察它。这将是我的第一选择。

如果您确实想使用 @Model 来表示可变列表,请使用选项 #2 中的 ModelList

您可能还想更改 ViewModel 以保存 MutableLiveData 引用。目前 ViewModel 持有的列表是不可观察的。有关 Android 架构组件中 ViewModelLiveData 的介绍,请查看 Android Basics course

关于android - 模型中列表的值已更新,但未反射(reflect)在可组合函数上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59125629/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com