Recyclerview EditText 引發的問題與解决方案

吳敬悅 2021-08-15 18:17:00 阅读数:228

本文一共[544]字,预计阅读时长:1分钟~
recyclerview edittext 解决方案 解决 方案

這是我參與8月更文挑戰的第4天,活動詳情查看:8月更文挑戰

問題

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
複制代碼

我們發現達到了我們想要的目標

版权声明:本文为[吳敬悅]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815181419045f.html