我的情况如下:假设我有一个应用程序,我在其中显示用户列表和他们的个人资料图片(类似于whatsapp(。首先,我加载用户列表,并观察用户的LiveData
。问题是,他们的个人资料图片的url没有附带getUsersList
API。相反,我必须实时执行另一个网络调用(在渲染列表时(,以便检索给定用户的配置文件图片,比如getUserPicByUserId
。
使用MVVM设计模式,我完成了以下实现:
-
In片段类I从
getUsersList
API加载用户的列表。对于每个用户项,profilePicUrl
为Null。 -
在Adapter/ViewHolder类I中,检查
profilePicUrl
是否为null。如果是这样,我将使用侦听器(MyAdapterListener
(为给定的用户调用getUserPic
API。 -
当
getUserPic
API的响应就绪时,我更新ViewHolder中的用户项(请参见lambda函数onUrlLoaded: (url: String) -> Unit
(并加载图像。
问题:这种方法导致所有项目都得到相同的图像,因为userProfilePicLiveData
的观察者总是在监听每个用户项目。每个项目都会加载最后一个检索到的url。
添加:
片段中回调onUrlLoaded(profilePicUrl)
后的viewModel.userProfilePicLiveData.removeObservers(lifecycleOwner)
也将不起作用。
我在这个问题上花了一些时间,但找不到解决办法。
对于这种情况,什么是合适的方法?如何在渲染每个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)
})
}
}
每次loadProfilePicByUserId(userId: String)
从API获取新的配置文件映像时,ViewModel都应更新存储在usersListLiveData
中的列表。您的Fragment应该观察usersListLiveData
,并在数据发生更改时通知适配器(使用adapter.notifyDataSetChanged()
或等效方法(。
需要注意的是,User
是一个复杂对象,而usersListLiveData
是这些复杂对象的列表。您不能只更改相关列表项中的一个属性profilePicUrl
。您需要调用类似usersListLiveData.value = newUserList
的东西,否则MutableLiveData的观测者将不会启动。当你只更改一个复杂对象的一个属性时,观察者不会开火。您需要拨打setValue()
或postValue()
。