注册

Recyclerview EditText 引发的问题与解决方案



问题


GIF 2021-8-5 7-27-00.gif


我使用简单的魔法就让各位大佬的财富值减少了,我们来看看谷歌公司是怎样做到的。


我们知道 Recyclerview 是有复用机制的,一般复用的个数是一个屏幕多一点的数量,比如我这里就是 16


默认情况,找到产生问题的原因


也就是我们不做任何修改,只看文本监听里面输出内容看看打印的日志,先看监听代码:


input?.addTextChangedListener(object : TextWatcher {
init {
Log.i(TAG, "Holder init: -------------------------------")
}
override fun beforeTextChanged(s: CharSequence?,start: Int,count: Int,after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Log.i(TAG, "onTextChanged ${d.name}: $s")
}
override fun afterTextChanged(s: Editable?) {}
})

现在我们看初始化的日志:


I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: Holder init: -------------------------------

现在我滑动到底看看对应的日志输出:


I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 杰夫·贝索斯: 618
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 602
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 伯纳德·阿尔诺及家族: 595
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 比尔·盖茨: 590
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 马克·扎克伯格: 553
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 沃伦·巴菲特: 530
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 拉里·埃里森: 519
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 拉里·佩奇: 505
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 谢尔盖·布林: 499
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 穆克什·安巴尼: 484
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 阿曼西奥·奥特加: 464
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 弗朗索瓦丝·贝当古·迈耶斯及家族: 464
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 钟睒睒: 454
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 史蒂夫·鲍尔默: 451
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 马化腾: 441
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 杰夫·贝索斯: 418
I/吴敬悦: onTextChanged 艾丽斯·沃尔顿: 418
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 392
I/吴敬悦: onTextChanged 吉姆·沃尔顿: 392
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 伯纳德·阿尔诺及家族: 390
I/吴敬悦: onTextChanged 罗伯·沃尔顿: 390
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 比尔·盖茨: 382
I/吴敬悦: onTextChanged 迈克尔·布隆伯格: 382
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 马克·扎克伯格: 377
I/吴敬悦: onTextChanged 黄峥: 377
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 沃伦·巴菲特: 369
I/吴敬悦: onTextChanged 麦肯齐·斯科特: 369
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 拉里·埃里森: 356
I/吴敬悦: onTextChanged 丹尼尔·吉尔伯特: 356
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 拉里·佩奇: 351
I/吴敬悦: onTextChanged 高塔姆·阿达尼及家族: 351
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 谢尔盖·布林: 345
I/吴敬悦: onTextChanged 菲尔·耐特及家族: 345
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 穆克什·安巴尼: 345
I/吴敬悦: onTextChanged 马云: 345
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 阿曼西奥·奥特加: 337
I/吴敬悦: onTextChanged 查尔斯·科赫: 337
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 弗朗索瓦丝·贝当古·迈耶斯及家族: 335
I/吴敬悦: onTextChanged 茱莉亚·科赫及家族: 335
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 卡洛斯·斯利姆·埃卢及家族: 330
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 史蒂夫·鲍尔默: 320
I/吴敬悦: onTextChanged 迈克尔·戴尔: 320
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 马化腾: 317
I/吴敬悦: onTextChanged 柳井正及家族: 317
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 弗朗索瓦·皮诺特及家族: 313
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 杰夫·贝索斯: 313
I/吴敬悦: onTextChanged 艾丽斯·沃尔顿: 313
I/吴敬悦: onTextChanged 大卫·汤姆森及家族: 313
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 296
I/吴敬悦: onTextChanged 吉姆·沃尔顿: 296
I/吴敬悦: onTextChanged 贝亚特·海斯特和小卡尔·阿尔布雷希特: 296
I/吴敬悦: Holder init: -------------------------------

可以看到默认情况下是只执行实例化的操作,而文本改变的监听却没有,那我滚动的时候发现文本监听触发了,其实我并没有改变文本,那为啥会这样子呢,当然就是因为复用导致的,由于复用所以原本已经被赋值的还会被赋值,这个时候就会触发文本改变监听,同时由于每一个监听器被多个数据使用,所以这里的财富所对应的名字也是不同的。在滑动过程中我们也发现 TextWatcher 被多次实例化,但又不是跟数据条数所对应。我们知道如果 TextWatcher 的个数跟数据量相同,是不是就可以解决数据乱的问题呢,我们尝试让每一项数据都有独一无二的 TextWatcher


我新建了一个类:


class OwnTextWatcher(private val name: String): TextWatcher {
init {
Log.i(Adapter.TAG, "init name: $name-------------------")
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Log.i(Adapter.TAG, "onTextChanged ${name}: $s")
}

override fun afterTextChanged(s: Editable?) {}
}

初始化的日志:


I/吴敬悦: init name: 杰夫·贝索斯-------------------
I/吴敬悦: init name: 埃隆·马斯克-------------------
I/吴敬悦: init name: 伯纳德·阿尔诺及家族-------------------
I/吴敬悦: init name: 比尔·盖茨-------------------
I/吴敬悦: init name: 马克·扎克伯格-------------------
I/吴敬悦: init name: 沃伦·巴菲特-------------------
I/吴敬悦: init name: 拉里·埃里森-------------------
I/吴敬悦: init name: 拉里·佩奇-------------------
I/吴敬悦: init name: 谢尔盖·布林-------------------
I/吴敬悦: init name: 穆克什·安巴尼-------------------
I/吴敬悦: init name: 阿曼西奥·奥特加-------------------
I/吴敬悦: init name: 弗朗索瓦丝·贝当古·迈耶斯及家族-------------------

这是的个数刚好差不多是一屏的数量,再看我滑动的日志输出:


I/吴敬悦: init name: 钟睒睒-------------------
I/吴敬悦: init name: 史蒂夫·鲍尔默-------------------
I/吴敬悦: init name: 马化腾-------------------
I/吴敬悦: init name: 卡洛斯·斯利姆·埃卢及家族-------------------
I/吴敬悦: onTextChanged 杰夫·贝索斯: 618
I/吴敬悦: init name: 艾丽斯·沃尔顿-------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 602
I/吴敬悦: init name: 吉姆·沃尔顿-------------------
I/吴敬悦: onTextChanged 伯纳德·阿尔诺及家族: 595
I/吴敬悦: init name: 罗伯·沃尔顿-------------------
I/吴敬悦: onTextChanged 比尔·盖茨: 590
I/吴敬悦: init name: 迈克尔·布隆伯格-------------------
I/吴敬悦: onTextChanged 马克·扎克伯格: 553
I/吴敬悦: init name: 黄峥-------------------
I/吴敬悦: onTextChanged 沃伦·巴菲特: 530
I/吴敬悦: init name: 麦肯齐·斯科特-------------------
I/吴敬悦: onTextChanged 拉里·埃里森: 519
I/吴敬悦: init name: 丹尼尔·吉尔伯特-------------------
I/吴敬悦: onTextChanged 拉里·佩奇: 505
I/吴敬悦: init name: 高塔姆·阿达尼及家族-------------------
I/吴敬悦: onTextChanged 谢尔盖·布林: 499
I/吴敬悦: init name: 菲尔·耐特及家族-------------------
I/吴敬悦: init name: 马云-------------------
I/吴敬悦: onTextChanged 阿曼西奥·奥特加: 464
I/吴敬悦: init name: 查尔斯·科赫-------------------
I/吴敬悦: onTextChanged 弗朗索瓦丝·贝当古·迈耶斯及家族: 464
I/吴敬悦: init name: 茱莉亚·科赫及家族-------------------
I/吴敬悦: onTextChanged 钟睒睒: 454
I/吴敬悦: init name: 孙正义-------------------
I/吴敬悦: onTextChanged 史蒂夫·鲍尔默: 451
I/吴敬悦: init name: 迈克尔·戴尔-------------------
I/吴敬悦: onTextChanged 马化腾: 441
I/吴敬悦: init name: 柳井正及家族-------------------
I/吴敬悦: onTextChanged 卡洛斯·斯利姆·埃卢及家族: 423
I/吴敬悦: init name: 弗朗索瓦·皮诺特及家族-------------------
I/吴敬悦: onTextChanged 穆克什·安巴尼: 418
I/吴敬悦: init name: 大卫·汤姆森及家族-------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 392
I/吴敬悦: onTextChanged 吉姆·沃尔顿: 392
I/吴敬悦: init name: 贝亚特·海斯特和小卡尔·阿尔布雷希特-------------------
I/吴敬悦: onTextChanged 杰夫·贝索斯: 390
I/吴敬悦: onTextChanged 艾丽斯·沃尔顿: 390
I/吴敬悦: init name: 王卫-------------------
I/吴敬悦: onTextChanged 比尔·盖茨: 382
I/吴敬悦: onTextChanged 迈克尔·布隆伯格: 382
I/吴敬悦: init name: 米丽娅姆·阿德尔森-------------------
I/吴敬悦: onTextChanged 马克·扎克伯格: 377
I/吴敬悦: onTextChanged 黄峥: 377
I/吴敬悦: init name: 何享健及家族-------------------
I/吴敬悦: onTextChanged 沃伦·巴菲特: 369
I/吴敬悦: onTextChanged 麦肯齐·斯科特: 369
I/吴敬悦: init name: 迪特尔·施瓦茨-------------------
I/吴敬悦: onTextChanged 拉里·埃里森: 356
I/吴敬悦: onTextChanged 丹尼尔·吉尔伯特: 356
I/吴敬悦: init name: 张一鸣-------------------
I/吴敬悦: onTextChanged 拉里·佩奇: 351
I/吴敬悦: onTextChanged 高塔姆·阿达尼及家族: 351
I/吴敬悦: init name: 乔瓦尼·费列罗-------------------
I/吴敬悦: onTextChanged 谢尔盖·布林: 345
I/吴敬悦: onTextChanged 菲尔·耐特及家族: 345
I/吴敬悦: init name: 阿兰·韦特海默-------------------
I/吴敬悦: onTextChanged 马云: 345
I/吴敬悦: init name: 杰拉德·韦特海默-------------------
I/吴敬悦: onTextChanged 阿曼西奥·奥特加: 337
I/吴敬悦: onTextChanged 查尔斯·科赫: 337
I/吴敬悦: init name: 李嘉诚-------------------
I/吴敬悦: onTextChanged 弗朗索瓦丝·贝当古·迈耶斯及家族: 335
I/吴敬悦: onTextChanged 茱莉亚·科赫及家族: 335
I/吴敬悦: init name: 秦英林-------------------
I/吴敬悦: onTextChanged 钟睒睒: 330
I/吴敬悦: onTextChanged 孙正义: 330
I/吴敬悦: init name: 丁磊-------------------
I/吴敬悦: onTextChanged 史蒂夫·鲍尔默: 320
I/吴敬悦: onTextChanged 迈克尔·戴尔: 320
I/吴敬悦: init name: 莱恩·布拉瓦特尼克-------------------
I/吴敬悦: onTextChanged 马化腾: 317
I/吴敬悦: onTextChanged 柳井正及家族: 317
I/吴敬悦: init name: 李兆基-------------------
I/吴敬悦: onTextChanged 卡洛斯·斯利姆·埃卢及家族: 313
I/吴敬悦: onTextChanged 弗朗索瓦·皮诺特及家族: 313
I/吴敬悦: init name: 杰奎琳·马尔斯-------------------
I/吴敬悦: onTextChanged 穆克什·安巴尼: 313
I/吴敬悦: onTextChanged 大卫·汤姆森及家族: 313
I/吴敬悦: init name: 约翰·马尔斯-------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 296
I/吴敬悦: onTextChanged 吉姆·沃尔顿: 296
I/吴敬悦: onTextChanged 贝亚特·海斯特和小卡尔·阿尔布雷希特: 296
I/吴敬悦: init name: 杨惠妍及家族-------------------

我核对了初始化的数量,发现跟数据是相同的,说明的确初始化了这么多,那为啥还是有这种现象呢,我们知道的是其实输入框的节点对象并不是跟数据量相同,而是要看复用了多少,其实对于一个手机来说基本上每次初始化相同列表所实例化的是相同或相似的(我没有验证)。既然如此那么即便我们 TextWatcher 的数量是跟数据量相同,但由于本身 EditText 的数量就只有那么几个,要同时保存那么多数量的 TextWatcher 是不现实的,如果真要保存,那么只能是一个 EditText 实例保存了多份 TextWatcher 。我们可以去看一下 addTextChangedListener 的源码:


public void addTextChangedListener(TextWatcher watcher) {
if (mListeners == null) {
mListeners = new ArrayList<TextWatcher>();
}

mListeners.add(watcher);
}

我们发现果然是添加,并不是替换,也就是一个 EditText 是可以对应多个 TextWatcher 的,于是我就想为啥这样设计呢,其实我觉得原因就是有这样的需求,就是有可能一个文本的改变有多处监听,这也是普遍的需求。我们假设如果这个地方只有一个监听,也就是一对一的关系,那么我们这里是不是可以实现我们想要的功能呢,答案的否定的,如果真是这样的话,那么只会有那么几个是有效的,而且当后面的监听把前面的代替以后,前面的压根就不能正常工作。


寻找我们想要的答案


根据前面的分析与理解,我们知道产生这种问题的原因,现在我们的目标就是对症下药。


我们知道总是只有那么几个实例,只要数量多到达到复用的情况,那么就会出现一对多的情况,其实在复用的情况下我们是不希望一对多的,毕竟我们改变一个的时候就是改变一个,既然这样,那么我们可以尝试让监听的数量刚好跟 EditText 的实例数量相同;我们知道每一个 RecyclerView.ViewHolder 实例化都会执行 init ,而且在这里面总是跟 RecyclerView.ViewHolder 的数量相同,所以我们把监听的工作放到这里面进行。但是又会出现一个新的问题,也就是刚才我们说的如果只有一个的话,那么数据的一对一怎么保证呢,我想到一个方法,因为我们知道数据每一次渲染都会执行 onBindViewHolder 这个函数,也就是每一次数据都会在这里改变,那么我使用一个全局的变量保存数据,只要执行了 onBindViewHolder 这个函数,那么就更新数据,这样就解决问题了,下面看代码:


class Adapter: RecyclerView.Adapter<Adapter.Holder>() {
companion object {
const val TAG = "吴敬悦"
private var currentData: Data? = null
}
var list: ArrayList<Data> = arrayListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_test_adapter, parent, false)
return Holder(view)
}

override fun onBindViewHolder(holder: Holder, position: Int) {
holder.bind(list[position], position)
}

override fun getItemCount(): Int = list.size

inner class Holder(view: View): RecyclerView.ViewHolder(view) {
private var text: TextView? = null
private var input: EditText? = null
init {
text = view.findViewById(R.id.titleText)
input = view.findViewById(R.id.input)
input?.addTextChangedListener(object : TextWatcher {
init {
Log.i(TAG, "Holder init: -------------------------------")
}
override fun beforeTextChanged(s: CharSequence?,start: Int,count: Int,after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
Log.i(TAG, "onTextChanged ${currentData?.name}: $s")
}
override fun afterTextChanged(s: Editable?) {}
})
}
fun bind(d: Data, position: Int) {
currentData = d
text?.text = d.name
input?.setText(d.wealth.toString())
}
}
}

下面看一下日志输出,当初始化时:


I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 杰夫·贝索斯: 1770
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 埃隆·马斯克: 1510
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 伯纳德·阿尔诺及家族: 1500
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 比尔·盖茨: 1240
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 马克·扎克伯格: 970
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 沃伦·巴菲特: 960
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 拉里·埃里森: 930
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 拉里·佩奇: 915
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 谢尔盖·布林: 890
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 穆克什·安巴尼: 845
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 阿曼西奥·奥特加: 770
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 弗朗索瓦丝·贝当古·迈耶斯及家族: 736

当我们滑到底的日志:


I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 钟睒睒: 689
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 史蒂夫·鲍尔默: 687
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 马化腾: 658
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 卡洛斯·斯利姆·埃卢及家族: 628
I/吴敬悦: onTextChanged 艾丽斯·沃尔顿: 618
I/吴敬悦: onTextChanged 吉姆·沃尔顿: 602
I/吴敬悦: onTextChanged 罗伯·沃尔顿: 595
I/吴敬悦: onTextChanged 迈克尔·布隆伯格: 590
I/吴敬悦: onTextChanged 黄峥: 553
I/吴敬悦: onTextChanged 麦肯齐·斯科特: 530
I/吴敬悦: onTextChanged 丹尼尔·吉尔伯特: 519
I/吴敬悦: Holder init: -------------------------------
I/吴敬悦: onTextChanged 高塔姆·阿达尼及家族: 505
I/吴敬悦: onTextChanged 菲尔·耐特及家族: 499
I/吴敬悦: onTextChanged 马云: 484
I/吴敬悦: onTextChanged 查尔斯·科赫: 464
I/吴敬悦: onTextChanged 茱莉亚·科赫及家族: 464
I/吴敬悦: onTextChanged 孙正义: 454
I/吴敬悦: onTextChanged 迈克尔·戴尔: 451
I/吴敬悦: onTextChanged 柳井正及家族: 441
I/吴敬悦: onTextChanged 弗朗索瓦·皮诺特及家族: 423
I/吴敬悦: onTextChanged 大卫·汤姆森及家族: 418
I/吴敬悦: onTextChanged 贝亚特·海斯特和小卡尔·阿尔布雷希特: 392
I/吴敬悦: onTextChanged 王卫: 390
I/吴敬悦: onTextChanged 米丽娅姆·阿德尔森: 382
I/吴敬悦: onTextChanged 何享健及家族: 377
I/吴敬悦: onTextChanged 迪特尔·施瓦茨: 369
I/吴敬悦: onTextChanged 张一鸣: 356
I/吴敬悦: onTextChanged 乔瓦尼·费列罗: 351
I/吴敬悦: onTextChanged 阿兰·韦特海默: 345
I/吴敬悦: onTextChanged 杰拉德·韦特海默: 345
I/吴敬悦: onTextChanged 李嘉诚: 337
I/吴敬悦: onTextChanged 秦英林: 335
I/吴敬悦: onTextChanged 丁磊: 330
I/吴敬悦: onTextChanged 莱恩·布拉瓦特尼克: 320
I/吴敬悦: onTextChanged 李兆基: 317
I/吴敬悦: onTextChanged 杰奎琳·马尔斯: 313
I/吴敬悦: onTextChanged 约翰·马尔斯: 313
I/吴敬悦: onTextChanged 杨惠妍及家族: 296

我们发现达到了我们想要的目标

0 个评论

要回复文章请先登录注册