Android:MVVM-RecyclerView动态数据加载



我的情况如下:假设我有一个应用程序,我在其中显示用户列表和他们的个人资料图片(类似于whatsapp(。首先,我加载用户列表,并观察用户的LiveData。问题是,他们的个人资料图片的url没有附带getUsersListAPI。相反,我必须实时执行另一个网络调用(在渲染列表时(,以便检索给定用户的配置文件图片,比如getUserPicByUserId

使用MVVM设计模式,我完成了以下实现:

  1. In片段类I从getUsersListAPI加载用户的列表。对于每个用户项,profilePicUrl为Null。

  2. Adapter/ViewHolder类I中,检查profilePicUrl是否为null。如果是这样,我将使用侦听器(MyAdapterListener(为给定的用户调用getUserPicAPI。

  3. getUserPicAPI的响应就绪时,我更新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不附带getUsersListAPI,因此默认情况下为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()

最新更新