0

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

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.

因此,我在 中也有以下代码,在ViewModel中可以观察到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

    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观察站点列表的代码:

        // 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 发生任何变化时更新一次,所以没关系。

我不知道如何防止这种情况发生,没有在 Fragment 或 ViewModel 中保存一个列表来比较观察到的列表,这是我试图避免的(试图遵循 MVVM 架构)..

编辑:为清楚起见,我添加了 myRecyclerViewAdapter以显示该功能的setList作用:

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}")
    }

}

4

1 回答 1

0

您可以使用postValue而不是setValue

如果您在主线程执行发布的任务之前多次调用此方法,则只会分派最后一个值。

于 2021-01-23T19:41:10.463 回答