Search code examples
javaandroidandroid-recyclerviewnotifydatasetchangedaddtextchangedlistener

RecyclerView .addTextChangedListener gives multiple positions


I'm trying to create a RecyclerView that contains an EditText in each row. Once the text is changed i'd like it to show the position with println. It runs fine until i run .notifyDataSetChanged() in my main method. After that it prints multiple positions even though just one EditText was changed.

@Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        holder.etName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

           }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                System.out.println(position);
            }
        });
}

before: et0 changed = I/System.out: 0 <- correct

after: et0 changed = I/System.out: 2 AND I/System.out: 0 <- wrong


Solution

  • This is happening because you're adding a new TextWatcher everytime your ViewHolder is binded with the view. The RecyclerView is correctly recycling the ViewHolders, so when it uses an already created one, it just adds a new TextWatcher.

    You can modify the way you're using this, by registering the TextWatcher when you create the ViewHolder, so you won't have to deal with continuos listener bindings. So, in your ViewHolder constructor, after you bind your EditText, you can add the TextWatcher, like this:

    this.etName = root.findViewById(R.id.yourEtID); // I'm sure you're doing something very similar to this in your VH's constructor
    this.etName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
           }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                System.out.println(etName.getTag());
            }
        });
    

    You'll notice that this time I've used a etName.getTag() to read the position. This is very helpful, because now you can just modify your onBindViewHolder to look like this:

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        holder.etName.setTag(position);
    }
    

    Just feel free to ask if anything is unclear.