【问题标题】:Android: MVVM - RecyclerView dynamic data loadAndroid:MVVM - RecyclerView 动态数据加载
【发布时间】:2020-09-01 14:10:37
【问题描述】:

我的情况如下:假设我有一个应用程序,其中显示用户列表及其个人资料图片(类似于 whatsapp)。 首先我加载用户列表,并观察LiveData 的用户。问题是,他们的个人资料图片的 url 不附带 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)并加载图像。

问题:这种方法导致所有项目都获得相同的图像,因为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


【解决方案1】:

每次loadProfilePicByUserId(userId: String) 从 API 中提取新的个人资料图片时,您的 ViewModel 都应该更新存储在 usersListLiveData 中的列表。您的 Fragment 应观察 usersListLiveData 并在数据更改时通知适配器(使用 adapter.notifyDataSetChanged() 或等效项)。

需要注意的是User 是一个复杂对象,usersListLiveData 是这些复杂对象的列表。您不能只更改相关列表项中的一个属性profilePicUrl。您需要调用 usersListLiveData.value = newUserList 之类的东西,否则 MutableLiveData 的观察者不会触发。当您仅更改复杂对象的单个属性时,观察者不会触发。您需要致电setValue()postValue()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    • 2017-02-10
    • 1970-01-01
    • 2019-03-07
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多