android Выбранное состояние ListAdapter с использованием MVVM - PullRequest
0 голосов
/ 11 июля 2020

Я не могу найти хороший способ для выбранного состояния в ListAdapter с использованием архитектуры MVVM. Я также использую репозиторий LiveData, реализованный для firestore @Doug Stevenson

Я пытаюсь избежать notifyDataSetChanged(), так как это вызывает заметный сбой в пользовательском интерфейсе

В течение нескольких дней попыток это мой последний попытка, которая не работает, поскольку oldList сохраняется как ссылка, поэтому DiffUtil.ItemCallback не обнаруживает никаких изменений. Похоже, мне нужно выполнить глубокое клонирование в список, но я считаю, что должно быть решение с более высокой производительностью.

MainViewModel

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
    }

    ....
}

MainFragment

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
}

MainAdapter

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

1 Ответ

0 голосов
/ 11 июля 2020

Я не уверен, что это лучшая практика или даже если она соответствует MVVM, но учитывая, что это было лучшее, о чем я мог думать. Если у вас есть другие идеи, дайте мне знать и не спешите переходить на более раннюю версию

в viewmodel, я сохранил выбранный список и для удобства изменил идентификатор последнего элемента notifyItemChanged() в MainAdapter. В MainAdapter я ввожу выбранное состояние в onBindViewHolder, используя Data Binding переменную.

MainViewModel

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

    ...

}

MainFragment

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

MainAdapter

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

        ...
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...