0

我找不到使用 MVVM 架构在 ListAdapter 中选择状态的好方法。我还在使用@Doug Stevenson 为 Firestore 实现的存储库 LiveData

我试图避免notifyDataSetChanged(),因为在 UI 上造成明显的故障

在几天的尝试中,这是我最后一次尝试,因为oldList它被保存为参考,所以DiffUtil.ItemCallback没有检测到任何变化。似乎我需要对列表进行深度克隆,但我认为必须有更好的性能解决方案。

主视图模型

class MainViewModel @ViewModelInject constructor(private val glistRepo: GlistRepository): ViewModel() {
  
    private val selected = MutableLiveData<HashMap<String,Glist>>()

    init {
        selected.value = HashMap()
    }

    fun getSelected(): LiveData<HashMap<String,Glist>> {
        return selected
    }

    fun onItemClicked(glist: Glist) {
        selected.value?.set(glist.id, glist)
        selected.value = selected.value //inform observers
    }

    ....
}

主要片段

viewModel.getSelected().observe(viewLifecycleOwner, Observer {
            adapter.setSelected(it.keys.toList())
        })

RepositoryCommon

对于选定的状态,我添加了一个sel对象,但由于我不想将其添加到数据库中,所以我添加到了包装器接口

interface QueryItem<T> {
    val item: T
    val id: String
    var sel: Boolean //The selected state
}

主适配器

class MainAdapter(private val clickListener: MainAdapterListener):
    ListAdapter<QueryItem<Glist>, MainAdapter.ViewHolder>(asyncDifferConfig) {

    private val selected = HashSet<String>()
    private var oldList: List<QueryItem<Glist>>? = ArrayList()

    fun setSelected(list: List<String>) {
        selected.clear()
        selected.addAll(list)
        //Instead of using notifyDataSetChanged() i am trying to trigger the diffUtil. Not working!
        submitList(oldList)
    }

    override fun submitList(list: List<QueryItem<Glist>>?) {
        if (selected.size != 0 && oldList != null && oldList!!.isNotEmpty()) {
            var id: String
            for (i in oldList!!.indices) {
                oldList!![i].sel = false
                id = oldList!![i].id
                 selected.forEach { key ->
                    if (id == key) {
                        oldList!![i].sel = true
                    }
                }
            }
        }

        oldList = list
        super.submitList(oldList)
    }

QueryItemDiffCallback

open class QueryItemDiffCallback<T> : DiffUtil.ItemCallback<QueryItem<T>>() {
    override fun areItemsTheSame(oldItem: QueryItem<T>, newItem: QueryItem<T>): Boolean {
        return (oldItem.id == newItem.id && oldItem.sel == newItem.sel)
    }

    @SuppressLint("DiffUtilEquals")  // equals() is OK for data classes
    override fun areContentsTheSame(oldItem: QueryItem<T>, newItem: QueryItem<T>): Boolean {
        return (oldItem.item == newItem.item && oldItem.sel == newItem.sel)
    }
}
4

1 回答 1

0

我不确定这是最佳实践,或者即使它与 MVVM 匹配,但考虑到,这是我能想到的最好的。如果您有其他想法,请告诉我,不要急于降级

在我保存了选定的列表和最后一个项目的viewmodelid 更改为方便notifyItemChanged()MainAdapter在 MainAdapter 中,我onBindViewHolder使用Data Binding变量注入所选状态。

主视图模型

class MainViewModel @ViewModelInject constructor(private val glistRepo: GlistRepository): ViewModel() {


    private val selected = MutableLiveData<HashMap<String,Glist>>()
    var lastSelectedId = ""
        private set

    init {
        selected.value = HashMap()
    }

    private fun toggleSelected(glist:Glist) {
        lastSelectedId = glist.id
        if (selected.value!!.containsKey(glist.id)) {
            selected.value?.remove(lastSelectedId)
        } else {
            selected.value?.set(lastSelectedId, glist)
        }
        selected.value = selected.value //Notify observers
    }

    fun getSelected(): LiveData<HashMap<String,Glist>> {
        return selected
    }

    fun onItemClicked(glist: Glist, short: Boolean) {
        if (!short || selected.value!!.size != 0) {
            toggleSelected(glist)
        } else {
            selected.value?.clear()
            _navigateToItem.value = glist.id
        }
    }

    ...

}

主要片段

viewModel.getSelected().observe(viewLifecycleOwner, Observer {
            adapter.setSelected(it.keys.toList(),viewModel.lastSelectedId)
        })

主适配器

class MainAdapter(private val clickListener: MainAdapterListener):
    ListAdapter<QueryItem<Glist>, MainAdapter.ViewHolder>(asyncDifferConfig) {

    ...

    private val selected = HashSet<String>()

    fun setSelected(list: List<String>, lastChangedId: String) {
        selected.clear()
        selected.addAll(list)
        for (i in 0 until currentList.size) {
            if (currentList[i].id == lastChangedId) {
                notifyItemChanged(i)
                return
            }
        }
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item,clickListener,selected.contains(item.id))
    }

    ...

    class ViewHolder private constructor(val binding: ListMainBinding): RecyclerView.ViewHolder(binding.root){

        fun bind(item: QueryItem<Glist>, clickListener: MainAdapterListener, isSelected: Boolean) {
            binding.glist = item.item
            binding.clickListener = clickListener
            binding.isSelected = isSelected
            binding.executePendingBindings()
        }

        ...
    }
}
于 2020-07-11T19:14:38.423 回答