RecyclerView中EditText数据错乱问题总结

RecyclerView中EditText数据错乱问题总结

之前项目中存在多个需要在RecylerView中显示多个EditText,并需要从editText获取数据的场景。

在使用时出现在第一个EditText输入数据后,往下滑动时,出现另一个EditText显示和第一个EditText相同的内容,获取到的内容也出现了重复,出现了显示和数据的错乱的问题。

问题分析

该数据错乱的根本原因是因为视图的复用机制导致的。当RecyclerView滑动屏幕时,会将离开屏幕的Item进行回收和复用,这样可以在滑动时提高效率和减少内存的占用。

简单地说就是当屏幕中的item占满屏幕以后,我们滑动listview的时候,第一个item退出屏幕,最后一个item进入屏幕:

View getView(final int position, View convertView, ViewGroup parent) 

这个时候,getview中convertview就不为空了,它的值就是第一个item的view,我们一般都会通过ViewHolder来缓存item,就不用重复findid了,直接就可以使用缓存的控件了。

当一个EditText被用户焦点所在时,用户的输入会直接通过Focus机制改变当前EditText的内容,而这个操作是不能保存到RecyclerView所对应数据的。当这个EditText滑动到屏幕外界面去并被重新利用时,用户之前输入的内容都会保留,导致显示的EditText中的文本不对应RecyclerView中的实际数据,因此会导致数据错乱。
针对这个问题记录了几种方法来进行解决:

方法一:强制的停用Recyclerview的复用。

holder.setIsRecyclable(false)

在RecyclerView中,强制停用复用机制确实可以解决包含EditText导致的数据错乱问题,但是这种做法会带来如下几个缺点:

  1. 内存占用问题:停止回收和复用机制后,RecyclerView会一直保存所有的Item视图和数据,这会导致内存的占用高于实际需要,并可能导致性能下降;

  2. 性能问题:RecyclerView之所以采用复用机制,是因为在滑动过程中回收和复用视图可以避免重复创建和销毁视图,并提高滑动性能。如果关闭了该机制,可能会导致RecyclerView性能下降;

  3. 维护困难:如果禁用复用机制,将导致项目中参杂着大量的模板布局文件,需要一个个去维护。这将增加代码复杂度,导致项目难以维护;

因此,强制停用RecyclerView的复用机制虽然可以解决数据错乱问题,但是也会带来明显的性能问题、维护难度问题和内存占用问题的问题,需要根据实际情况进行选择。综合来看,不推荐使用该方法。

方法二:通过view的setTag()方法解决

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val item = list[position]

    with(holder.itemView) {
        //解决Adapter复用引起的EditText数据错误
        //1.判断EditText.getTag()获取标签是否为TextWatcher,
        if (editText.getEditText()?.tag is TextWatcher) {
            //2.移除监听文本监听器
            editText.getEditText()?.removeTextChangedListener(editText.getEditText()?.tag as TextWatcher)
        }
        //3.EditText.setText设置输入框文本
        editText.setText(content)

        val watcher: TextWatcher = object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
            override fun afterTextChanged(editable: Editable) {
                content = editable.toString()
                textChangeListener.invoke(item)
            }
        }

        //4.添加监听文本监听器和EditText.setTag设置标签
        editText.getEditText()?.addTextChangedListener(watcher)
        editText.getEditText()?.tag = watcher
    }
}

虽然通过 setTag() 方法解决 RecyclerView 复用问题是一种简单有效的方法,但也存在一些缺点:

  1. 内存占用:由于 setTag() 方法会将数据保存在 View 对象中,当 RecyclerView 中的视图较多时,会造成较大的内存占用,可能导致 APP 崩溃或 ANR。为了避免该问题的发生,可以尝试使用局部更新和只更新可见视图的方法来减少视图数量。

  2. 数据同步可能不及时:由于 setTag() 方法依赖于 RecyclerView 视图的复用机制,当 RecyclerView 视图被回收和重用时,视图中保存的 data 对象的引用也会变化,可能在视图的更新和更改中导致数据同步不及时,或更新后数据不正确的问题。

  3. 需要额外的处理和管理:复用问题的产生是由于 RecyclerView 视图的复用机制导致的,因此使用 setTag() 方法解决该问题本质上是绕过了该机制,在一定程度上增加了对数据的管理和缓存的处理难度和复杂性。这也需要我们动态地管理数据对象和 RecyclerView 视图显示的关系,保证 RecyclerView 视图显示的数据实时、有效和正确。

综上所述,虽然 setTag() 方法解决 RecyclerView 复用问题是一种便捷有效的方法,但我们需要根据项目需求和具体情况来选择解决 RecyclerView 复用问题的方法,并且在使用 setTag() 方法时需要考虑数据同步和管理等问题。

方法三:通过监听焦点来添加或移除Edittext的TextChangedListener来解决

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val data = mDatas[position]

    val textWatcher: TextWatcher = object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
        override fun afterTextChanged(s: Editable) {
            //这里处理数据
            //....
        }
    }
    
    holder.editText.setOnFocusChangeListener { v, hasFocus ->
        if (hasFocus) {
            holder.editText.addTextChangedListener(object : TextWatcher {
                override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
                override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                    data.content = s.toString()
                }
                override fun afterTextChanged(s: Editable?) {}
            })
        } else {
            holder.editText.removeTextChangedListener(holder.textWatcher)
        }
    }
}

通过监听焦点来添加或移除EditText的TextChangedListener是解决RecyclerView数据复用问题的一种有效方式。该方法实现简单,避免了 setTag() 方法的一些缺点,同时,该方法也适用于监听其他 View 的焦点变化,并根据需求实时更新数据的场景,例如 ListView、GridView 等。

不过,该方法也存在一些缺点:

  1. 焦点变化:该方法依赖于焦点的变化来动态添加、移除TextChangedListener。当焦点变化很频繁时,会影响RecyclerView的性能和操作体验。

  2. 数据显示问题:由于RecyclerView视图的复用机制仍然保持不变,所以在移除TextChangedListener之后,EditText的数据不会随着视图的消失而一同消失,而是会保留在视图中。如果使用默认的缓存机制,滑动离开、再回到该视图时,会出现数据错乱的问题。因此,为了解决这个问题,还需要进行额外的数据管理和缓存策略的设计。

因此,通过监听焦点来添加或移除EditText的TextChangedListener,是一种有效的解决RecyclerView数据复用问题的方式,但也需要在实现时需要考虑上述问题,对数据的管理和缓存策略进行合理设计。

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值