Я использую RecyclerView с ListAdapter, который использует DiffUtil для внутреннего отображения данных. Конечно, все данные были предоставлены ViewModel через LiveData
.
Теперь в каждом представлении элемента есть флажок, а я реализовал функцию выбора перетаскиванием, добавив RecyclerView.SimpleOnItemTouchListener
. Статус выбора теперь поддерживается адаптером, это не лучшая идея, так как статус будет утерян после поворота экрана.
Я хочу, чтобы ViewModel сохранял статус выбора, и это моя идея:
- Фрагмент вызовет ViewModel для изменения статуса при получении соответствующего обратного вызова.
- ViewModel изменит список данных (или создаст новый, если он неизменяемый) и опубликует в LiveData.
- Фрагмент получает изменение данных и отправляет его
ListAdapter
.
Проблема в том, что статус выбора может очень часто меняться, если пользователь активирует выделение перетаскиванием. DifferUtil
придется долго сравнивать, если данных много. В отличие от сохранения статуса внутри адаптера, теперь я не могу точно уведомить об изменении элемента, потому что я не знаю, какая позиция изменилась.
Основная идея MVVM - это представление, управляемое данными, но в этом случае мы потеряли дополнительную информацию (например, позицию), что привело к дополнительным вычислениям. Итак, какова лучшая практика?
Часть кода адаптера (на основе GroupListAdapter
, который расширяет ListAdapter
):
private val selectedGroups = mutableSetOf<Int>()
private val selectedImages = mutableSetOf<Image>()
override fun onBindGroupViewHolder(holder: StubViewHolder, groupIndex: Int, flattedPosition: Int) {
holder.setText(R.id.itemImageTitleTextView, getGroupItem(groupIndex).title)
}
override fun onBindChildViewHolder(holder: StubViewHolder, groupIndex: Int, childIndex: Int, flattedPosition: Int) {
val data = getChildItem(groupIndex, childIndex)
GlideApp.with(fragment).load(data.uri).into(holder.getView(R.id.itemImageView))
}
override fun onBindGroupViewHolder(holder: StubViewHolder, groupIndex: Int, flattedPosition: Int,
payloads: MutableList<Any>) {
holder.setGroupSelectionMode(isInSelectMode())
holder.setGroupChecked(selectedGroups.contains(flattedPosition))
}
override fun onBindChildViewHolder(holder: StubViewHolder, groupIndex: Int, childIndex: Int, flattedPosition: Int,
payloads: MutableList<Any>) {
if (isInSelectMode()) {
holder.setImageSelectionMode(true)
if (selectedImages.contains(getItem(flattedPosition))) {
holder.getView<MaskImageView>(R.id.itemImageView).isChecked = true
holder.setImageChecked(true)
} else {
holder.getView<MaskImageView>(R.id.itemImageView).isChecked = false
holder.setImageChecked(false)
}
} else {
holder.getView<MaskImageView>(R.id.itemImageView).isChecked = false
holder.setImageSelectionMode(false)
}
}
fun setSelected(position: Int, isSelected: Boolean, refreshItem: Boolean = true) {
if (!isChildItem(position)) {
return
}
if (isSelected) {
selectedImages.add(getItem(position) as Image)
} else {
selectedImages.remove(getItem(position))
}
if (refreshItem) {
notifyItemChanged(position, true)
}
onSelectChangedCallback?.invoke(selectedImages.size)
}
DiffUtil
будет сравнивать весь список данных, чтобы вычислить разницу. Есть около 1000 ~ 4000 элементов, так как это приложение будет работать на часовых устройствах, этот размер уже не маленький. - person Chenhe   schedule 19.02.2020