Android:MVVM-RecyclerView动态数据加载

问题描述

我的情况如下:假设我有一个应用程序,在其中显示用户列表及其个人资料照片(类似于whatsapp)。 首先,我加载用户列表,并观察LiveData用户。问题在于,他们的个人资料图片的网址不是getUsersList API附带的。相反,我必须进行另一个网络通话(在渲染列表的同时),以便检索给定用户的个人资料图片,例如getUserPicByUserId

使用MVVM设计模式,我执行了此实现:

  1. Fragment 类中,我从getUsersList API加载了用户列表。对于每个用户项目,profilePicUrl为Null。

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

  3. 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()