【发布时间】:2020-02-19 07:43:25
【问题描述】:
我正在使用带有 ListAdapter 的 RecyclerView,它在内部使用 DiffUtil 来显示数据。当然,所有数据都是 ViewModel 通过LiveData提供的。
现在每个项目视图都有一个CheckBox,同时我通过添加RecyclerView.SimpleOnItemTouchListener实现了拖动选择功能。选择状态现在由适配器维护,这不是一个好主意,因为屏幕旋转后状态会丢失。
我想让 ViewModel 保存选择状态,这是我的想法:
- Fragment 在收到相关回调时会调用 ViewModel 改变状态。
- ViewModel 将更改数据列表(如果它不可变,则创建一个新列表)并发布到 LiveData。
- Fragment 接收到数据变化并提交给
ListAdapter。
问题是如果用户激活拖动选择,选择状态可能会非常频繁地变化。 DifferUtil如果有很多数据就得花很长时间比较。与在适配器中保存状态不同,现在我无法准确通知项目更改,因为我不知道更改了哪个位置。
MVVM 的核心思想是数据驱动视图,但在这种情况下,我们丢失了额外的信息(例如位置),导致额外的计算。那么最佳做法是什么?
部分适配器代码(基于扩展ListAdapter的GroupListAdapter):
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)
}
【问题讨论】:
-
这很有趣。请发布您的适配器? RecyclerView 通常不会呈现完整的数据集,只是适合屏幕的数据加上一些额外的数据。另外,我们所说的数据量是多少?
-
@ror 我添加了一些代码,但我认为适配器并不重要。这是真的只有可见的项目会被渲染,但问题是
DiffUtil将比较整个数据列表来计算差异。大约有 1,000~4,000 个项目,由于这个应用程序将在手表设备上运行,所以这个大小已经不小了。
标签: android android-recyclerview android-viewmodel android-mvvm