Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/184.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 使用RecyclerView、ListAdapter和ViewModel保存选中状态的最佳实践_Android_Android Recyclerview_Android Viewmodel_Android Mvvm - Fatal编程技术网

Android 使用RecyclerView、ListAdapter和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将更改数据列表(如

我正在使用RecyclerView,在内部使用它来显示数据。当然,所有数据都是由ViewModel通过
LiveData
提供的

现在每个项目视图都有一个复选框,同时我通过添加
RecyclerView.SimpleOnItemTouchListener
实现了拖动选择功能。选择状态现在由适配器维护,这不是一个好主意,因为屏幕旋转后状态将丢失

我想让ViewModel保存选择状态,这是我的想法:

  • 当接收到相关回调时,Fragment将调用ViewModel来更改状态
  • ViewModel将更改数据列表(如果数据列表不可变,则创建一个新列表)并发布到LiveData
  • Fragment接收数据更改并将其提交给
    ListAdapter
  • 问题是,如果用户拖动选择,选择状态可能会非常频繁地更改<如果存在大量数据,则code>Differentil必须花费很长时间进行比较。与在适配器内保存状态不同,现在我无法准确地通知项目已更改,因为我不知道更改了哪个位置

    MVVM的核心思想是数据驱动视图,但在这种情况下,我们丢失了额外的信息(例如位置),导致了额外的计算。那么,最佳实践是什么

    适配器代码的一部分(基于扩展
    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)
        }