【发布时间】:2020-09-01 14:10:37
【问题描述】:
我的情况如下:假设我有一个应用程序,其中显示用户列表及其个人资料图片(类似于 whatsapp)。
首先我加载用户列表,并观察LiveData 的用户。问题是,他们的个人资料图片的 url 不附带 getUsersList API。相反,我必须立即进行另一个网络调用(在呈现列表时),以便检索给定用户的个人资料图片,比如说getUserPicByUserId。
使用 MVVM 设计模式,我做了这个实现:
-
在 Fragment 类中,我从
getUsersListAPI 加载 用户列表。对于每个用户项,profilePicUrl为 Null。 -
在 Adapter/ViewHolder 类中,我检查
profilePicUrl是否为 null。如果是这样,使用监听器 (MyAdapterListener),我为给定的用户调用getUserPicAPI。 -
当
getUserPicAPI 的响应准备好时,我更新 ViewHolder 中的用户项(参见 lambda 函数onUrlLoaded: (url: String) -> Unit)并加载图像。
问题:这种方法导致所有项目都获得相同的图像,因为userProfilePicLiveData 的观察者总是在监听每个用户项目。每个项目都会加载最后检索到的 url。
添加:
viewModel.userProfilePicLiveData.removeObservers(lifecycleOwner) 在 Fragment 中回调 onUrlLoaded(profilePicUrl) 后也不起作用。
我在这个问题上花了一些时间,但找不到解决方案。
对于这种情况,合适的方法是什么?如何在渲染每个RecyclerView 项时执行网络调用,并将结果发送回Adapter 以更新视图?
这是我到目前为止所做的简化代码:
模型用户
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)
})
}
}
【问题讨论】:
-
为什么你的 api 返回一个没有图片的用户?这似乎不是一个完整的用户对象,可能与您的问题无关
-
@a_local_nobody 没有什么可做的。该服务由第 3 方提供,我必须找到具有这些限制的解决方案。
-
为什么不等到完成两个 API 调用后再将数据发送到适配器?
-
@GavinWright 以什么方式?我尝试在
Interceptor层中执行此操作。迭代每个用户并为每个用户执行getProfilePic以完成User对象。但这增加了很多延迟......有更好的解决方法吗?
标签: android mvvm android-recyclerview android-adapter android-livedata