问题描述
我的情况如下:假设我有一个应用程序,在其中显示用户列表及其个人资料照片(类似于whatsapp)。
首先,我加载用户列表,并观察LiveData
个用户。问题在于,他们的个人资料图片的网址不是getUsersList
API附带的。相反,我必须进行另一个网络通话(在渲染列表的同时),以便检索给定用户的个人资料图片,例如getUserPicByUserId
。
使用MVVM设计模式,我执行了此实现:
-
在 Fragment 类中,我从
getUsersList
API加载了用户列表。对于每个用户项目,profilePicUrl
为Null。 -
在 Adapter / ViewHolder 类中,我检查
profilePicUrl
是否为 null 。如果是这样,我将使用侦听器(MyAdapterListener
)为给定的用户调用getUserPic
API。 -
当
getUserPic
API的响应准备就绪时,我在 ViewHolder 中更新用户项(请参阅lambda函数onUrlLoaded: (url: String) -> Unit
)并加载图像。 / p>
问题:这种方法导致所有项目都获得相同的图像,因为userProfilePicLiveData
的观察者始终在侦听每个用户项目。每个项目都会加载最后检索到的网址。
添加:
片段中的回调viewmodel.userProfilePicLiveData.removeObservers(lifecycleOwner)
之后的 onUrlLoaded(profilePicUrl)
也不起作用。
在这种情况下合适的方法是什么?呈现每个RecyclerView
项时如何执行网络调用,并将结果发送回适配器以更新视图?
这里是我到目前为止所做的简化代码:
模型用户
data class User(
val id: String,val name: String,val profilePicUrl: String? = null,// By default is null
...
)
profilePicUrl
不带有getUsersList
API,因此默认为Null
。
模型 UserProfilePic :
data class UserProfilePic(
val url: String
...
)
viewmodel 类的实现示例:
class Myviewmodel: viewmodel() {
val usersListLiveData = LiveData<List<User>>
val userProfilePicLiveData = LiveData<UserProfilePic>
fun loadUsers() {
// Network call...
usersListLiveData.value = usersList
}
fun loadProfilePicByUserId(userId: String) {
// Network call...
userProfilePicLiveData.value = userProfilePic
}
}
适配器类:
class RecyclerViewAdapter(val usersList: List<User>): RecyclerView.Adapter<MyViewHolder>() {
interface MyAdapterListener {
fun onLoadProfilePicUrl(
user: User,onUrlLoaded: (url: String) -> Unit
)
}
class HomeVenueViewHolder(
val listener: MyAdapterListener
) : RecyclerView.ViewHolder() {
fun bind(user: User) {
// Fill view list item ...
if (user.profilePicUrl is Null ) {
listener.onLoadProfilePicUrl(user) { url ->
user.profilePicUrl = url
// Load Image From the Url
}
} else {
// Valid url: Load image
}
}
}
}
片段类:
class MyFragment: Fragment(),MyAdapterListener {
val viewmodel: Myviewmodel
viewmodel.usersListLiveData.observe(viewLifecycleOwner,{ usersList ->
// Setup adapter and show list
})
fun loadUsers() {
viewmodel.loadUsers()
}
override fun onLoadProfilePicUrl(
user: User,onUrlLoaded: (url: String) -> Unit
) {
viewmodel.loadProfilePicByUserId(user.id)
viewmodel.userProfilePicLiveData.observe(viewLifecycleOwner,{ profilePicUrl ->
// This is a callback to Adapter
onUrlLoaded(profilePicUrl)
})
}
}
解决方法
每次usersListLiveData
从API中提取新的个人资料图片时,您的ViewModel应该更新存储在loadProfilePicByUserId(userId: String)
中的列表。您的片段应遵守usersListLiveData
并在数据更改时通知适配器(使用adapter.notifyDataSetChanged()
或同等功能)。
一个警告是User
是一个复杂对象,而usersListLiveData
是这些复杂对象的列表。您只需要在相关列表项中更改一个属性profilePicUrl
就无法摆脱。您需要调用usersListLiveData.value = newUserList
之类的东西,否则MutableLiveData的观察者将不会触发。当您仅更改复杂对象的单个属性时,观察者将不会触发。您需要致电setValue()
或postValue()
。