ListAdapter 获取正确的列表但不更新值

ListAdapter fetches the right list but does not update the values

我有以下功能 -

    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 对象,其中包含一个指示是否已创建新组的布尔值。

在编写这两个函数 ^ 的同一个片段中,我正在观察这个布尔值,如果值为真,我会触发对整个列表的重新获取 -

private fun observeSharedInformation() {
        sharedInformationViewModel.value.groupCreatedFlag.observe(requireActivity(), Observer { hasGroupBeenCreated ->
            if (!hasGroupBeenCreated) return@Observer
            sharedInformationViewModel.value.groupCreatedFlag.value = false
            Log.d("submitList", "groupCreatedFlag")
            initAdapter()

        })
    }

在我的另一个片段中的代码中的某个时刻,该片段也有我共享的 ViewModel 的一个实例,我触发了布尔 LiveData 的值更改 -

sharedInformationViewModel.value.groupCreatedFlag.value = true

这又会触发观察者,并重新获取我的组列表。

我面临的问题是,当重新获取新列表时(因为添加了一个新组)我确实获得了当前信息并且一切都应该 100% 正常,但是新数据 - 新创建的组 - 未出现。

新添加的数据在2种情况下出现在列表中-

  1. 我重启应用程序
  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)
        }
    })
}

您使用的是“新”ListAdapter吗?

import androidx.recyclerview.widget.ListAdapter

在这种情况下,我可以想出解决您问题的方法。但由于我对您的具体实施了解不多,所以这是基于我的假设,您必须验证它是否适用。

为此 ListAdapter 您必须实施 areItemsTheSameareContentsTheSame 方法。 我曾经遇到过类似的问题。我正在提交列表,但它没有更新视图中的列表。

我可以通过仔细检查我是如何比较内容是否相同来解决这个问题的。

对于您的比较功能,请考虑以下内容:

    override fun areContentsTheSame(oldItem: GroupModel, newItem: GroupModel): Boolean {
        // assuming GroupModel is a class
        // this comparison is most likely not getting the result you want
        val groupModelsAreMatching = oldItem == newItem // don't do this
        
        // for data classes it usually gives the expected result
        val exampleDataClassesMatch = oldItem.dataClass == newItem.dataClass
        // But: the properties that need to be compared need to be declared in the primary constructor
        // and not in the function body

        // compare all relevant custom properties
        val groupIdMatches = oldItem.groupId == newItem.groupId
        val groupNameMatches = oldItem.groupName == newItem.groupName
        val groupTaskCountMatches = oldItem.groupTaskCount == newItem.groupTaskCount
        val groupUsersFullNamesMatches = oldItem.groupUsersFullNames == newItem.groupUsersFullNames

        return groupIdMatches && groupNameMatches && groupTaskCountMatches && groupUsersFullNamesMatches
}

当然,您需要确保 areItemsTheSame。这里只需要比较groupIds即可。

你已经这样做了吗?

我找到问题了。

这与我的获取逻辑无关。

问题如下-

创建组时,我将一个新片段添加到后台堆栈并在完成后将其弹出。

删除组时,我在使用 popUpTopopUpToInclusive 的同时导航到我的主要片段 - 工作正常。

我需要使用导航而不是向后弹出堆栈才能看到新列表。

这花了我 3 天的时间才弄明白。哎呀