观察MediatorLiveData问题 [英] Observing MediatorLiveData Issue

查看:64
本文介绍了观察MediatorLiveData问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的ViewModel中有以下LiveData变量(简化示例):

I have the following LiveData variables in my ViewModel (simplified example):

val currentUser : LiveData<UserObject>
val allSites : LiveData<ArrayList<SiteObject>>
val filterSitesForUser : LiveData<Boolean>
val orderSitesByField : LiveData<String>
val orderSitesDirection : LiveData<Query.Direction>
val searchFilterSitesText : LiveData<String>

我正在尝试使用MediatorLiveData将一个数据流"连接到我的RecyclerView.

I'm trying to use MediatorLiveData to have one 'stream' of data connecting to my RecyclerView.

因此,我在ViewModel中也有以下代码,在Fragment中可以看到:

I therefore also have the following code in the ViewModel, which is observed in the Fragment:

   fun sitesList() : LiveData<ArrayList<SiteObject>> {

        val result = MediatorLiveData<ArrayList<SiteObject>>()
        
        result.addSource(currentUser) {
            result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
        }

        result.addSource(allSites) {
            result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
        }

        result.addSource(filterSitesForUser) {
            result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
        }
        
        result.addSource(orderSitesByField) {
            result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
        }

        result.addSource(orderSitesDirection) {
            result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
        }

        result.addSource(searchFilterSitesText) {
            result.value = combineSitesData(currentUser, allSites, filterSitesForUser, orderSitesByField, orderSitesDirection, searchFilterSitesText)
        }
        
        return result
    }

..与此同时,也在ViewModel中:

..along with this, also in the ViewModel:

    private fun combineSitesData (
        currentUser: LiveData<UserObject>,
        allSites: LiveData<ArrayList<SiteObject>>,
        filterSitesForUser: LiveData<Boolean>,
        orderSitesByField: LiveData<String>,
        orderSitesDirection: LiveData<Query.Direction>,
        searchFilterSitesText: LiveData<String>
    ) : ArrayList<SiteObject> {

        var sitesList = ArrayList<SiteObject>()
        val userId = currentUser.value?.userID

        if (userId == null || allSites.value == null) {
            Log.d(TAG, "combineSitesData() - currentUser or allSites value null")
            return sitesList
        }

        when (filterSitesForUser.value) {
            true -> sitesList.addAll(allSites.value!!.filter { site -> site.users.contains(userId) })
            false -> sitesList.addAll(allSites.value!!)
        }

        if (orderSitesDirection.value == Query.Direction.ASCENDING){
            when (orderSitesByField.value) {
                DATE_CREATED -> sitesList.sortBy { it.dateCreatedTimestamp }
                DATE_EDITED -> sitesList.sortBy { it.dateEditedTimestamp }
                SITE_TASKS -> sitesList.sortBy { it.siteTask }
                SITE_RATING -> sitesList.sortBy { it.siteRating }
                else -> sitesList.sortBy {it.siteReference}
            }
        }

        if (orderSitesDirection.value == Query.Direction.DESCENDING){
            when (orderSitesByField.value) {
                DATE_CREATED -> sitesList.sortByDescending { it.dateCreatedTimestamp }
                DATE_EDITED -> sitesList.sortByDescending { it.dateEditedTimestamp }
                SITE_TASKS -> sitesList.sortByDescending { it.siteTask }
                SITE_RATING -> sitesList.sortByDescending { it.siteRating }
                else -> sitesList.sortByDescending {it.siteReference}
            }
        }

        if (!searchFilterSitesText.value.isNullOrEmpty()) {
            var filteredList = ArrayList<SiteObject>()
            var filterPattern = searchFilterSitesText.value.toString().toLowerCase().trim()
            for (site in sitesList) {
                if(site.siteReference.toLowerCase().contains(filterPattern) || site.siteAddress.toLowerCase().contains(filterPattern)) {
                    filteredList.add(site)
                }
            }
            Log.d(TAG, "combineSitesData() - returned filteredList (size = ${filteredList.size})")
            return filteredList
        }

        Log.d(TAG, "combineSitesData() - returned sitesList (size = ${sitesList.size})")
        return sitesList
    }

这是Fragment中观察站点列表的代码:

This is the code in the Fragment which observes the list of Sites:

        // Observe ViewModel SitesList
        businessViewModel.sitesList().observe(viewLifecycleOwner, Observer { sitesList ->
            if (sitesList != null) {
                businessViewModel.currentUser.value?.userID?.let { sitesAdapter.setList(it, sitesList) }
                sitesAdapter.notifyDataSetChanged()
                Log.d (TAG, "setupViewModelObservers(): businessViewModel.sitesList().size = ${sitesList.size}" )
            }
        })

它似乎与第一个观察到的事实相反,它触发了六次,因此更新了我的RecylcerView六次-当这是一个很大的列表时,我认为这有点问题!

It seems to be working apart from the fact that when it is first observes, it fires off six times, which therefore updates my RecylcerView six times - when this is a large list, I see this being a bit of an issue!

初始化之后,它仅在LiveData发生任何更改时才更新一次,所以没关系.

After it initialises, it only updates once when any of the LiveData's change, so that's ok.

我不确定如何避免这种情况的发生,而没有在Fragment或ViewModel中保存一个列表以将观察到的列表与之进行比较(我试图避免这种情况(因为尝试遵循MVVM架构).).

I'm not sure how to prevent this from happening, without holding a list in the Fragment or ViewModel to compare observed list to, which I am trying to avoid (as trying to follow MVVM architecture)..

为清楚起见,我添加了RecyclerViewAdapter来显示功能setList的作用:

for clarity, I have added my RecyclerViewAdapter to show what the function setList does:

class SitesRecyclerViewAdapter(
    private var currentUserId: String,
    private val onItemClickedListener: (SiteObject) -> Unit,
    private val onItemLongClickedListener: (SiteObject) -> Boolean
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private var sitesList = ArrayList<SiteObject>()

    class SiteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        private val listItemBinding = SitesListItemBinding.bind(itemView)

        fun bind(
            userId: String,
            site: SiteObject,
            clickListener: (SiteObject) -> Unit,
            longClickListener: (SiteObject) -> Boolean
        ) {

            // Set text fields
            listItemBinding.sitesItemTitleText.text = site.siteReference
//            listItemBinding.sitesItemProjectsText.text = site.recentProjectsText TODO: Create way of showing recent projects with arrays

            //Reset Icons visibility
            listItemBinding.sitesItemTaskImageView.visibility = View.INVISIBLE
            listItemBinding.sitesItemRating1ImageView.visibility = View.INVISIBLE
            listItemBinding.sitesItemRating2ImageView.visibility = View.INVISIBLE
            listItemBinding.sitesItemRating3ImageView.visibility = View.INVISIBLE
            listItemBinding.sitesItemFavouriteImageView.visibility = View.GONE

            //Set sitePriority Icon visibility
            if (site.siteTask) listItemBinding.sitesItemTaskImageView.visibility = View.VISIBLE

            //Set siteRating Icon visibility
            when(site.siteRating){
                1 -> listItemBinding.sitesItemRating1ImageView.visibility = View.VISIBLE
                2 -> listItemBinding.sitesItemRating2ImageView.visibility = View.VISIBLE
                3 -> listItemBinding.sitesItemRating3ImageView.visibility = View.VISIBLE
            }

            //Set siteFavourite Icon visibility
            if (site.users.contains(userId)) listItemBinding.sitesItemFavouriteImageView.visibility = View.VISIBLE

            // Set Listeners
            listItemBinding.sitesItemCardview.setOnClickListener { clickListener(site) }
            listItemBinding.sitesItemCardview.setOnLongClickListener { longClickListener(site) }
            // Not set map listener?
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        // LayoutInflater: takes ID from layout defined in XML.
        // Instantiates the layout XML into corresponding View objects.
        // Use context from main app -> also supplies theme layout values!
        val inflater = LayoutInflater.from(parent.context)
        // Inflate XML. Last parameter: don't immediately attach new view to the parent view group
        val view = inflater.inflate(R.layout.sites_list_item, parent, false)
        return SiteViewHolder(view)
    }

    override fun getItemCount(): Int = sitesList.size

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        // Populate ViewHolder with data that corresponds to the position in the list
        // which we are told to load
        (holder as SiteViewHolder).bind(
            currentUserId,
            sitesList[position],
            onItemClickedListener,
            onItemLongClickedListener
        )
    }

    fun setList(userID: String, observedSites: ArrayList<SiteObject>) {
        currentUserId = userID
        sitesList = observedSites
        notifyDataSetChanged()  
        Log.d(TAG, "setList(), observedSites size = ${observedSites.size}")
    }

}

推荐答案

您可以使用postValue代替setValue

如果在主线程执行发布的任务之前多次调用此方法,则仅 最后一个值将被调度.

If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.

这篇关于观察MediatorLiveData问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆