模型中列表的值已更新,但未反映在可组合函数上

Kus*_*ave 5 android android-jetpack android-jetpack-compose

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

@Model
data class UsersState(var users: ArrayList<UserModel> = ArrayList())
Run Code Online (Sandbox Code Playgroud)

活动

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

}
Run Code Online (Sandbox Code Playgroud)

视图模型

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可组合函数

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

                            }
                        }
                    }
                }

            }

        }
    }

Run Code Online (Sandbox Code Playgroud)

整个源代码可以在这里找到

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

Sea*_*lan 9

哎呀!

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

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

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

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

@Model
class MyImmutableModel(val state: ModelState())
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

@Model
class UsersState(var users: List<UserModel> = listOf())
Run Code Online (Sandbox Code Playgroud)

然后当你想更新它时,你必须分配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
}
Run Code Online (Sandbox Code Playgroud)

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

在这种情况下,由于数据结构是List您正在连接的不可变类型的性能可能是不可接受的。但是,如果您持有一个不可变的data class选项,那么这个选项是一个不错的选择,因此我将其包含在内以保持完整性。

修复选项 #2:使用 ModelList

对于这个用例,Compose 有一个特殊的可观察列表类型。您可以使用代替 anArrayList并且对列表的任何更改都可以通过 compose 观察到。

@Model
class UsersState(val users: ModelList<UserModel> = ModelList())
Run Code Online (Sandbox Code Playgroud)

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

相关:嵌套@Model 类

值得注意的是,您可以嵌套@Model类,这就是该ModelList版本的工作方式。回到开头的示例,如果您将两个类都注释为 @Model,那么所有属性都将在 Compose 中可见。

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

@Model
class MyModel(var state: ModelState())
Run Code Online (Sandbox Code Playgroud)

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

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

TL;DR 选择哪个选项

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

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


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


2ja*_*222 0

您的模型没有被观察到,因此更改不会得到反映。 在本文的“将其全部放在一起”部分下添加了列表。

val list = +memo{ calculation: () -> T}
Run Code Online (Sandbox Code Playgroud)

您的列表示例:

@Composable
fun test(supplier: UserState) {
   val list = +memo{supplier.users}
   ListConsumer(list){
       /* Do other stuff for your usecase */
   }
}
Run Code Online (Sandbox Code Playgroud)