【发布时间】:2020-08-13 16:44:59
【问题描述】:
我有以下功能-
private fun fetchGroupData(callback: (groupModelList: List<GroupModel>) -> Unit) {
val groupModelList = mutableListOf<GroupModel>()
groupViewmodel.getAllGroupEntities().observeOnce(requireActivity(), Observer { groupEntityList ->
groupEntityList.forEach { groupEntity ->
/*
We iterate though all of the available groups,
for each group we get all of it's groupMembers models
*/
val groupName = groupEntity.groupName
val groupId = groupEntity.id
taskViewmodel.getGroupTaskCounter(groupId).observeOnce(requireActivity(), Observer { groupTaskCount ->
/*
For each group we observe it's task counter
*/
groupViewmodel.getGroupMembersForGroupId(groupId).observeOnce(requireActivity(), Observer { groupMembers ->
/*
For each group, we iterate through all of the groupMembers and for each of them we use it's userId
to fetch the user model, getting it's full name and adding it to a list of group users full name.
*/
val groupUsersFullNames = mutableListOf<String>()
groupMembers.forEach { groupMember ->
val memberId = groupMember.userId
groupViewmodel.getGroupParticipantForUserId(memberId).observeOnce(requireActivity(), Observer { groupUser ->
groupUsersFullNames.add(groupUser.fullName)
/*
When the groupUsersFullNames size matches the groupMembers size, we can add a model to our list.
*/
if (groupUsersFullNames.size == groupMembers.size)
groupModelList.add(GroupModel(groupId, groupName, groupTaskCount, groupUsersFullNames))
/*
When the new list matches the size of the group list in the DB we call the callback.
*/
if (groupModelList.size == groupEntityList.size)
callback(groupModelList)
})
}
})
})
}
})
}
下面的函数正在使用它-
private fun initAdapter() {
fetchGroupData { groupModelList ->
if (groupModelList.isEmpty()) {
binding.groupsListNoGroupsMessageTitle.setAsVisible()
binding.groupsListNoGroupsMessageDescription.setAsVisible()
return@fetchGroupData
}
binding.groupsListNoGroupsMessageTitle.setAsGone()
binding.groupsListNoGroupsMessageDescription.setAsGone()
val newList = mutableListOf<GroupModel>()
newList.addAll(groupModelList)
adapter.submitList(groupModelList)
Log.d("submitList", "submitList")
binding.groupsListRecyclerview.setAdapterWithItemDecoration(requireContext(), adapter)
}
}
这 2 个函数代表从我的本地数据库中获取组列表到 RecyclerView。
为了在创建新组时收到通知,我持有一个共享 ViewModel 对象,该对象带有一个指示是否已创建新组的布尔值。
在编写这两个函数 ^ 的同一个片段中,我正在观察这个布尔值,如果值为 true,我会触发整个列表的重新获取 -
private fun observeSharedInformation() {
sharedInformationViewModel.value.groupCreatedFlag.observe(requireActivity(), Observer { hasGroupBeenCreated ->
if (!hasGroupBeenCreated) return@Observer
sharedInformationViewModel.value.groupCreatedFlag.value = false
Log.d("submitList", "groupCreatedFlag")
initAdapter()
})
}
在我的代码中的某个时刻,在另一个 Fragment 中也有我的共享 ViewModel 的实例,我触发了我的 Boolean LiveData 的值更改 -
sharedInformationViewModel.value.groupCreatedFlag.value = true
这又会触发观察者,并重新获取我的组列表。
我面临的问题是,在重新获取新列表时(因为添加了一个新组),我确实获得了当前信息,并且一切都应该 100% 正常工作,但是新数据 - 新创建的组- 不出现。
新增的数据在2种情况下出现在列表中-
- 我重新启动应用程序
- 再次触发该功能 - 现在发生的情况是我看到了包含先前新添加组的列表,但没有出现要添加的最新组。
这个问题有一个例外 - 如果组列表为空,当我提交包含一个组的列表时,确实会出现要添加的第一个组。
我错过了什么?
编辑 -
这是我的适配器。
我正在使用一个名为 DefaultAdapterDiffUtilCallback 的自定义调用,它需要一个模型来实现为每个模型定义唯一 ID 的接口,以便我可以比较新旧模型。
class GroupsListAdapter(
private val context: Context,
private val onClick: (model : GroupModel) -> Unit
) : ListAdapter<GroupModel, GroupsListViewHolder>(DefaultAdapterDiffUtilCallback<GroupModel>()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupsListViewHolder {
val binding = GroupsListViewHolderBinding.inflate(LayoutInflater.from(context), parent, false)
return GroupsListViewHolder(binding)
}
override fun onBindViewHolder(holder: GroupsListViewHolder, position: Int) {
holder.bind(getItem(position), onClick)
}
override fun submitList(list: List<GroupModel>?) {
super.submitList(list?.let { ArrayList(it) })
}
}
/**
* Default DiffUtil callback for lists adapters.
* The adapter utilizes the fact that all models in the app implement the "ModelWithId" interfaces, so
* it uses it in order to compare the unique ID of each model for `areItemsTheSame` function.
* As for areContentsTheSame we utilize the fact that Kotlin Data Class implements for us the equals between
* all fields, so use the equals() method to compare one object to another.
*/
class DefaultAdapterDiffUtilCallback<T : ModelWithId> : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T) =
oldItem.fetchId() == newItem.fetchId()
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: T, newItem: T) =
oldItem == newItem
}
/**
* An interface to determine for each model in the app what is the unique ID for it.
* This is used for comparing the unique ID for each model for abstracting the DiffUtil Callback
* and creating a default general one rather than a new class for each new adapter.
*/
interface ModelWithId {
fun fetchId(): String
}
data class GroupModel(val id: String, val groupName: String, var tasksCounter: Int, val usersFullNames: List<String>) : ModelWithId {
override fun fetchId(): String = id
}
编辑 2.0 -
我的observeOnce() 分机-
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
})
}
【问题讨论】:
标签: android android-recyclerview android-livedata android-listadapter