Allan Banis
Allan Banis

Reputation: 386

Android RecyclerView -Multiple Edittext change simultaneously

I have a RecyclerView that has many cards that hold 4 EditText. When I input values in one EditText of one card, it fills the same value in random cards. Surprisingly it does not jump EditTexts for example:

If I input values in edittext1 of card1 it would fill the same value into edittext1 in card8 and if I change the value in card8 it will change the value back in card1. Could someone please tell me why this is happening.

Thank You in Advance

Here is my code:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {    
private String[] mDataset;
public String[][] data = new String[30][4];


public static class ViewHolder extends RecyclerView.ViewHolder {
     // each data item is just a string in this case
     public LinearLayout mCardView;

     public ViewHolder(LinearLayout v) {
        super(v);
        mCardView = v;
    }
}

// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(String[] myDataset) {
    mDataset = myDataset;
}

// Create new views (invoked by the layout manager)
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                               int viewType) {

    LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
            .inflate(R.layout.cards, parent, false);

    ViewHolder vh = new ViewHolder(v);
    return vh;
}


// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element
    final EditText txt = (EditText) holder.mCardView.findViewById(R.id.ip1_text);
    final EditText txt2 = (EditText) holder.mCardView.findViewById(R.id.ip2_text);

    final EditText txt3 = (EditText) holder.mCardView.findViewById(R.id.ip3_text);
    final EditText txt4 = (EditText) holder.mCardView.findViewById(R.id.ip4_text);
    txt.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) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            data[position][0] = s.toString();
            Log.d("DATA" + position + "0", s.toString());
        }
    });
    txt2.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) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            data[position][1] = s.toString();
            Log.d("DATA" + position + "1", s.toString());
        }
    });
    txt3.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) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            data[position][2] = s.toString();
            Log.d("DATA" + position + "2", s.toString());
        }
    });
    txt4.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) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            data[position][3] = s.toString();
            Log.d("DATA" + position + "3", s.toString());
        }
    });

    TextView t = (TextView) holder.mCardView.findViewById(R.id.serNo);
    t.setText(mDataset[position]);


}

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
    return mDataset.length;
}

public String[][] getData() {
    return data;
}  

And this is my card xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:weightSum="1">
<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
    android:id="@+id/card_view"
    android:layout_width="375dp"
    android:layout_height="wrap_content"
    android:layout_margin="4dp"
    android:backgroundTint="#eeeeee"
    card_view:cardCornerRadius="4dp">

    <TextView
        android:id="@+id/serNo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:text="number"
        android:layout_margin="20dp"
        android:textColor="@color/cardview_dark_background" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center">

        <EditText
            android:id="@+id/ip1_text"
            android:layout_width="100dp"
            android:layout_height="30dp"
            android:layout_alignParentLeft="true"
            android:layout_marginTop="20dp"
            android:background="@color/cardview_light_background"
            android:inputType="number"
            android:maxLength="2" />

        <EditText
            android:id="@+id/ip3_text"
            android:layout_width="80dp"
            android:layout_height="30dp"
            android:layout_below="@+id/ip1_text"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"
            android:background="@color/cardview_light_background"
            android:inputType="number"
            android:maxLength="1" />

        <EditText
            android:id="@+id/ip2_text"
            android:layout_width="100dp"
            android:layout_height="30dp"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_toRightOf="@+id/ip1_text"
            android:background="@color/cardview_light_background"
            android:inputType="number"
            android:maxLength="2" />

        <EditText
            android:id="@+id/ip4_text"
            android:layout_width="80dp"
            android:layout_height="30dp"
            android:layout_below="@+id/ip2_text"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="40dp"
            android:layout_marginTop="10dp"
            android:layout_toRightOf="@+id/ip3_text"
            android:background="@color/cardview_light_background"
            android:inputType="number"
            android:maxLength="1" />
    </RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

Added to card one

Automatically it also appears in card 10

Upvotes: 3

Views: 9006

Answers (5)

RahulGunani
RahulGunani

Reputation: 81

This is Issue For Recycler View Recycle Just Override these function

 @Override
  public long getItemId(int position) {
   return position;
  } 

 @Override public int getItemViewType(int position) {
 return position;
 }

Upvotes: 0

Whitebird
Whitebird

Reputation: 238

When you set data you should use getAdapterPosition() instead of position. This is because when the recyclerview scrolled, it recycled and the position will not get the correct value.

            data[getAdapterPosition()][1] = s.toString();

Upvotes: 0

Volodymyr Zakharov
Volodymyr Zakharov

Reputation: 93

I used to OnFocusChangeListener in onBindViewHolder for adapter

        editText.setText(mValues.get(position).getValue());
        editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus)
                    mValues.get(position).setValue(editText.getText().toString());
            }
        });

Upvotes: 0

Nika Kurdadze
Nika Kurdadze

Reputation: 2512

That happens because the views inside a RecyclerView are being recycled. Inside the onBindViewHolder you need to set the specific texts to the EditTexts at the specific position.

  1. Initialize your Views inside the ViewHolder and add the TextWatchers there as the whole philosophy of the RecyclerView is to reuse the views :

    public static class ViewHolder extends RecyclerView.ViewHolder {
    
        EditText txt;
        EditText txt2;
        EditText txt3;
        EditText txt4;
    
        TextView serNoTxt;
    
        public ViewHolder(LinearLayout mCardView) {
            super(mCardView);
            txt = (EditText) mCardView.findViewById(R.id.ip1_text);
            txt2 = (EditText) mCardView.findViewById(R.id.ip2_text);
            txt3 = (EditText) mCardView.findViewById(R.id.ip3_text);
            txt4 = (EditText) mCardView.findViewById(R.id.ip4_text);
    
            setNoTxt = (TextView) mCardView.findViewById(R.id.serNo);
    
            txt.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) {
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    data[getAdapterPosition()][0] = s.toString();
                    Log.d("DATA" + getAdapterPosition() + "0", s.toString());
                }
                });
            txt2.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) {
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    data[getAdapterPosition()][1] = s.toString();
                    Log.d("DATA" + getAdapterPosition() + "1", s.toString());
                }
            });
            txt3.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) {
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    data[getAdapterPosition()][2] = s.toString();
                    Log.d("DATA" + getAdapterPosition() + "2", s.toString());
                }
            });
            txt4.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) {
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    data[getAdapterPosition()][3] = s.toString();
                    Log.d("DATA" + getAdapterPosition() + "3", s.toString());
                }
            });
        }
    }
    
  2. Set the appropriate text to the EditTexts in the onBindViewHolder :

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        holder.txt1.setText(data[position][0]);
        holder.txt2.setText(data[position][1]);
        holder.txt3.setText(data[position][2]);
        holder.txt4.setText(data[position][3]);
    
        holder.setNoTxt.setText(mDataset[position]);
    }
    

Upvotes: 10

Doron Yakovlev Golani
Doron Yakovlev Golani

Reputation: 5480

You updated the data without notifying the adapter. This could create inconsistencies. So after the data is changed you should call notifyItemChanged().

For example:

    @Override
    public void afterTextChanged(Editable s) {
        data[position][3] = s.toString();
        Log.d("DATA" + position + "3", s.toString());
        notifyItemChanged(position);
    }

One more suggestion I have is to reuse the code so you won't have to copy paste so much and fix in many places (for example, all of your TextWatcher objects are basically the same).

Upvotes: 1

Related Questions