Почему при изменении текста edittext в recycleriew изменяются 3 других?

Написал код который по факту должен сохранять значения из edittext который находится в recyclerview:

@Override
    public void onBindViewHolder(@NonNull NewRoomViewHolder holder, @SuppressLint("RecyclerView") int position) {
        holder.newRoomText.setText(newRooms.get(position).getText());

        holder.newRoomCount.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                onEditTextChanged(position, s.toString());
                Toast toast = Toast.makeText(context,
                        String.valueOf(position), Toast.LENGTH_LONG);
                toast.show();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }
    public void onEditTextChanged(int position, String charSeq) {
        if (!Objects.equals(charSeq, "")) {
            widgets.put("widget" + String.valueOf(position + 1), Integer.parseInt(charSeq));
        }
    }

однако обновлялось несколько editext. Объясню:

Когда пользователь вводит значение в edittext с position 1, вызывается функция onTextChanged у позиций: 11, 14, 1 (всего 15 edittext'ов). Всегда последней идёт правильная позиция. Т.е. вместо сохранения в список значения из 1, он сохраняет ещё и для 11, 14.

Можно ли это как-либо исправить?


Ответы (1 шт):

Автор решения: Wlad

это классическое поведение для Recycler'а, к сожалению.

О проблеме

сама проблема заключется в том, что у Ресайклера, на самом деле, ограниченное кол-во элементов. примерно = кол-во видимых элементов на экране + 1-2 штуки запаса сверху и снизу.
и когда вы скролите ресайклер - эти элементы переиспользуются (вызывается onBindViewHolder)
а т.к. addTextChangedListener - это добавление слушателя в массив слушателей (они копятся под капотом), то и происходит такое нестандартное поведение.

Решение классическое

Повесить addTextChangedListener НЕ в методе onBindViewHolder, а в методе onCreateViewHolder
onBindViewHolder - это метод, который вызывается каждый раз, когда элемент надо перерисовать а вот onCreateViewHolder - вызывается 1 раз и сразу для всех.
в целом, хорошим тоном является даже setOnClickListener именно в onCreateViewHolder

Решение для варинтов "общего для всех Ресайклера"

есть различные решения, чтобы создать 1 уникальный адаптер и скрамливать ему разные элементы. но у всех их одно общее - недоступность к методоу onCreateViewHolder
т.е. у нас в доступе только onBindViewHolder
но и тут решение красивое:
создайте ваш объект TextWatcher как переменную. а в onBindViewHolder, для вашего EditText повесьте слушатель фокуса.
если поле в фокусе - addTextChangedListener(myWatcher)
если фокус сняли - removeTextChangedListener(myWatcher)

→ Ссылка