首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >观察MediatorLiveData问题

观察MediatorLiveData问题
EN

Stack Overflow用户
提问于 2021-01-24 02:11:59
回答 1查看 182关注 0票数 0

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

代码语言:javascript
复制
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

因此,我在Fragment中观察到的ViewModel中也有以下代码

代码语言:javascript
复制
   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

代码语言:javascript
复制
    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中的代码:

代码语言:javascript
复制
        // 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六次-当这是一个很大的列表时,我认为这是一个小问题!

在初始化之后,它只在LiveData的任何变化时更新一次,所以这是可以的。

我不确定如何防止这种情况的发生,除非在片段或ViewModel中持有一个列表来与观察到的列表进行比较,这是我试图避免的(因为试图遵循MVVM架构)。

编辑:为了清楚起见,我添加了我的RecyclerViewAdapter,以显示函数setList的作用:

代码语言:javascript
复制
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}")
    }

}

EN

回答 1

Stack Overflow用户

发布于 2021-01-24 03:41:10

您可以使用postValue而不是setValue

如果在主线程执行已发送任务之前多次调用此方法,则只会调度最后一个值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65862724

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档