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导致的数据错乱问题,但是这种做法会带来如下几个缺点:
-
内存占用问题:停止回收和复用机制后,RecyclerView会一直保存所有的Item视图和数据,这会导致内存的占用高于实际需要,并可能导致性能下降;
-
性能问题:RecyclerView之所以采用复用机制,是因为在滑动过程中回收和复用视图可以避免重复创建和销毁视图,并提高滑动性能。如果关闭了该机制,可能会导致RecyclerView性能下降;
-
维护困难:如果禁用复用机制,将导致项目中参杂着大量的模板布局文件,需要一个个去维护。这将增加代码复杂度,导致项目难以维护;
因此,强制停用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 复用问题是一种简单有效的方法,但也存在一些缺点:
-
内存占用:由于 setTag() 方法会将数据保存在 View 对象中,当 RecyclerView 中的视图较多时,会造成较大的内存占用,可能导致 APP 崩溃或 ANR。为了避免该问题的发生,可以尝试使用局部更新和只更新可见视图的方法来减少视图数量。
-
数据同步可能不及时:由于 setTag() 方法依赖于 RecyclerView 视图的复用机制,当 RecyclerView 视图被回收和重用时,视图中保存的 data 对象的引用也会变化,可能在视图的更新和更改中导致数据同步不及时,或更新后数据不正确的问题。
-
需要额外的处理和管理:复用问题的产生是由于 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 等。
不过,该方法也存在一些缺点:
-
焦点变化:该方法依赖于焦点的变化来动态添加、移除TextChangedListener。当焦点变化很频繁时,会影响RecyclerView的性能和操作体验。
-
数据显示问题:由于RecyclerView视图的复用机制仍然保持不变,所以在移除TextChangedListener之后,EditText的数据不会随着视图的消失而一同消失,而是会保留在视图中。如果使用默认的缓存机制,滑动离开、再回到该视图时,会出现数据错乱的问题。因此,为了解决这个问题,还需要进行额外的数据管理和缓存策略的设计。
因此,通过监听焦点来添加或移除EditText的TextChangedListener,是一种有效的解决RecyclerView数据复用问题的方式,但也需要在实现时需要考虑上述问题,对数据的管理和缓存策略进行合理设计。