Android 使用RecyclerView、ListAdapter和ViewModel保存选中状态的最佳实践
我正在使用RecyclerView,在内部使用它来显示数据。当然,所有数据都是由ViewModel通过Android 使用RecyclerView、ListAdapter和ViewModel保存选中状态的最佳实践,android,android-recyclerview,android-viewmodel,android-mvvm,Android,Android Recyclerview,Android Viewmodel,Android Mvvm,我正在使用RecyclerView,在内部使用它来显示数据。当然,所有数据都是由ViewModel通过LiveData提供的 现在每个项目视图都有一个复选框,同时我通过添加RecyclerView.SimpleOnItemTouchListener实现了拖动选择功能。选择状态现在由适配器维护,这不是一个好主意,因为屏幕旋转后状态将丢失 我想让ViewModel保存选择状态,这是我的想法: 当接收到相关回调时,Fragment将调用ViewModel来更改状态 ViewModel将更改数据列表(如
LiveData
提供的
现在每个项目视图都有一个复选框,同时我通过添加RecyclerView.SimpleOnItemTouchListener
实现了拖动选择功能。选择状态现在由适配器维护,这不是一个好主意,因为屏幕旋转后状态将丢失
我想让ViewModel保存选择状态,这是我的想法:
ListAdapter
ListAdapter
的GroupListAdapter
):
private val selectedGroups=mutableSetOf()
private val selectedImages=mutableSetOf()
覆盖onBindGroupViewHolder(holder:StubViewHolder,groupIndex:Int,flattedPosition:Int){
holder.setText(R.id.itemImageTitleTextView,getGroupItem(groupIndex).title)
}
覆盖onBindChildViewHolder(holder:StubViewHolder,groupIndex:Int,childIndex:Int,flattedPosition:Int){
val数据=getChildItem(groupIndex,childIndex)
GlideApp.with(fragment).load(data.uri).into(holder.getView(R.id.itemImageView))
}
覆盖onBindGroupViewHolder(holder:StubViewHolder,groupIndex:Int,flattedPosition:Int,
有效负载:可变列表){
holder.setGroupSelectionMode(isInSelectMode())
holder.setGroupChecked(selectedGroups.contains(平坦位置))
}
覆盖onBindChildViewHolder(holder:StubViewHolder,groupIndex:Int,childIndex:Int,flattedPosition:Int,
有效负载:可变列表){
如果(isInSelectMode()){
holder.setImageSelectionMode(真)
if(selectedImages.contains(getItem(flattedPosition))){
holder.getView(R.id.itemImageView).isChecked=true
holder.setImageChecked(真)
}否则{
holder.getView(R.id.itemImageView).isChecked=false
holder.setImageChecked(假)
}
}否则{
holder.getView(R.id.itemImageView).isChecked=false
holder.setImageSelectionMode(错误)
}
}
fun setSelected(位置:Int,isSelected:Boolean,刷新项:Boolean=true){
如果(!isChildItem(位置)){
返回
}
如果(当选){
选择图像。添加(获取项目(位置)作为图像)
}否则{
选择图像。删除(获取项目(位置))
}
如果(刷新项目){
notifyItemChanged(位置,true)
}
onSelectChangedCallback?.invoke(SelecteImage.size)
}
可能是我弄错了,但看起来您没有使用分页功能?根据您的数据性质,您可以使用的任何子数据源。然后,DiffUtils将对更小的数据子集进行操作。您可以将所选项目(我假设它可以不止一个)偏移到viewmodel,将它们保持为状态,甚至可以动态地将原始数据包装到数据源本身内部具有选择指示的包装器中。因此,您的适配器将是纯数据驱动的。可能是我弄错了,但看起来您没有使用分页?根据您的数据性质,您可以使用的任何子数据源。然后,DiffUtils将对更小的数据子集进行操作。您可以将所选项目(我假设它可以不止一个)偏移到viewmodel,将它们保持为状态,甚至可以动态地将原始数据包装到数据源本身内部具有选择指示的包装器中。因此,您的适配器将是纯数据驱动的。这很有趣。请张贴您的适配器?RecyclerView通常不会呈现完整的数据集,只呈现适合屏幕的数据和一些额外的数据。另外,我们谈论的数据量是多少?@ror我添加了一些代码,但我认为适配器并不重要。这是真的,只会呈现可见项,但问题是DiffUtil
将比较整个数据列表以计算差异。大约有1000~4000件物品,因为这个应用程序将在手表设备上运行,所以这个尺寸不再小了。这很有趣。请张贴您的适配器?RecyclerView通常不会呈现完整的数据集,只呈现适合屏幕的数据和一些额外的数据。另外,我们谈论的数据量是多少?@ror我添加了一些代码,但我认为适配器并不重要。这是真的,只会呈现可见项,但问题是DiffUtil
将比较整个数据列表以计算差异。大约有1000~4000件物品,因为这个应用程序将在手表设备上运行,所以这个尺寸不再小了。一些问题:1。分页不支持在加载数据后修改数据。2.因为1,我必须使用另一个变量来保存状态,这意味着我仍然保持数据和视图之间的状态-没有数据驱动。3.除了1,即使使用分页,数据也会越来越大。假设用户加载了10个页面,每个提交仍然会与总体比较进行比较
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)
}