我正在创建将jetpack与mvvm一起使用的演示项目,我创建了包含用户列表的模型类。这些用户显示在列表中,并且顶部有一个按钮,单击该按钮会将新用户添加到列表中... 当用户单击按钮时,lambda更新有关其的活动,活动调用viewmodel,该活动将数据添加到列表中并使用livedata更新回活动,现在,在模型接收到新数据之后,它不会更新有关它的可组合函数,因此也不会更新列表未更新。 这是代码
@Model
data class UsersState(var users: ArrayList<UserModel> = ArrayList())
活动
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() })
}
}
}
ViewModel
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仍在开发人员预览中,所以将不胜感激。 谢谢
答案 0 :(得分:4)
嗨!
此处来自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
),因此当您更改email
或username
时,Compose将永远不会触发重组。这是因为@Model
仅适用于带注释的类的属性。在此示例中,state
在Compose中是可观察到的,但是username
和email
只是常规字符串。
在这种情况下,您已经有LiveData
中的getUsers()
–您可以在撰写中看到这一点。我们尚未在开发版本中发布Compose观察,但是可以使用效果编写一个观察,直到我们发布观察方法为止。只要记住要删除onDispose {}
中的观察者即可。
如果您使用任何其他可观察类型,例如Flow
,Flowable
等,也是如此。您可以将它们直接传递到@Composable
函数中,并观察其效果而无需引入中间@Model
类。
许多开发人员更喜欢UI状态的不可变数据类型(像MVI这样的模式鼓励这样做)。您可以更新示例以使用不可变列表,然后为了更改列表,必须将其分配给users
属性,Compose可以观察到。
@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
,因此您不能将不可变类型的性能串联起来。但是,如果您持有一个不变的data class
,则此选项是一个不错的选择,因此出于完整性考虑,我将其包括在内。
Compose针对此用例具有特殊的 observable 列表类型。您可以使用ArrayList
代替,而列表的任何更改都可以通过撰写观察。
@Model
class UsersState(val users: ModelList<UserModel> = ModelList())
如果您使用ModelList
,则您在“活动”中编写的其余代码将正常运行,并且Compose
将能够直接观察对users
的更改。
值得注意的是,您可以嵌套@Model
类,这是ModelList
版本的工作方式。回到开头的示例,如果将两个类都注释为@Model,则所有属性都将在Compose中可见。
@Model
class ModelState(var username: String, var email: String)
@Model
class MyModel(var state: ModelState())
注意:此版本将@Model
添加到ModelState
,并且还允许在MyModel
由于@Model
使得可以通过compose观察到该类的所有属性,因此state
,username
和email
都可以观察。
在此代码中完全避免使用@Model
(选项#0)将避免引入仅用于Compose的重复模型层。由于您已经将状态保持在ViewModel
中并通过LiveData
进行了公开,因此您可以直接将LiveData
传递给它,以便在那里进行观察。这将是我的首选。
如果您要使用@Model
表示可变列表,请使用选项#2中的ModelList
。
您可能需要更改ViewModel,以同时容纳MutableLiveData引用。当前,ViewModel拥有的列表是不可观察的。有关Android体系结构组件中的ViewModel
和LiveData
的介绍,请查看Android Basics course。
答案 1 :(得分:0)
未观察到您的模型,因此不会反映出更改。 In this article在“全部放在一起”部分下,添加了列表。
from collections import defaultdict
# convert a dictionary to a defaultdict
d = defaultdict(lambda: None,d)
列表示例:
<ListView x:Name="corsiList" HasUnevenRows="True" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ctrls:InfoCorso Title="{Binding CorsiList.Nome}" Time="18:30" StyleColor="{Binding ButtonColor}">
<x:Arguments>
<x:Int32>Binding Subs</x:Int32>
<x:Int32>Binding Max</x:Int32>
</x:Arguments>
</ctrls:InfoCorso>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>