5

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

@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() })
        }
    }

}

视图模型

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)

                            }
                        }
                    }
                }

            }

        }
    }

完整的源代码可在此处获得

我不确定我是不是做错了什么,还是因为 jetpack compose 仍处于开发人员预览版中,所以非常感谢您的帮助.. 谢谢

4

2 回答 2

11

哎哟!

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

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

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

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

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

由于state变量是不可变的 ( ),因此当您更改orval时,Compose 永远不会触发重新组合。这是因为只适用于被注释的类的属性。在此示例中,在 Compose 中是可观察的,但和只是常规字符串。emailusername@Modelstateusernameemail

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

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

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

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

许多开发人员更喜欢 UI 状态的不可变数据类型(MVI 等模式鼓励这样做)。您可以更新您的示例以使用不可变列表,然后为了更改列表,您必须分配给usersCompose 可观察的属性。

@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则此选项是一个不错的选项,因此为了完整性,我将其包括在内。

修复选项 #2:使用 ModelList

对于这个用例, Compose 有一个特殊的可观察列表类型。您可以使用代替 anArrayList并且列表的任何更改都可以通过 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())

注意:此版本添加@ModelModelState,并且还允许重新分配状态MyModel

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

TL;DR 选择哪个选项

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

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


您可能还想更改 ViewModel 以保存 MutableLiveData 引用。目前 ViewModel 持有的列表是不可观察的。有关 Android 架构组件的介绍ViewModel和介绍,LiveData请查看Android 基础课程

于 2020-02-11T08:56:35.273 回答
0

您的模型未被观察到,因此不会反映更改。 在这篇文章中的“将所有内容放在一起”部分下,添加了列表。

val list = +memo{ calculation: () -> T}

您的列表示例:

@Composable
fun test(supplier: UserState) {
   val list = +memo{supplier.users}
   ListConsumer(list){
       /* Do other stuff for your usecase */
   }
}
于 2019-12-01T13:53:42.623 回答