Reputation: 99
I have a EditText
in RecyclerView
. When I edit the content of a EditText
and scroll up/down, that same content is displayed in another line.
Also, If I return to the same position, the EditText
is empty.
Here is the layout for every item of the RecyclerView
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F3F2F2"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/txt_jantri_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/quicksand_bold"
android:text="1"
android:textColor="@color/theme_color"
android:textSize="10sp" />
<EditText
android:id="@+id/ed_jantri_number"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:background="@drawable/shape_jantri_edittext"
android:fontFamily="@font/quicksand_bold"
android:gravity="center"
android:inputType="number"
android:maxLines="1"
android:minLines="1"
android:padding="5dp"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
Here is the Adapter class where I am setting text to the box number:
public class AdapterJantri extends RecyclerView.Adapter<AdapterJantri.Myholder> {
@NonNull
@Override
public Myholder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new Myholder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.single_jantri_view, viewGroup, false));
}
@Override
public void onBindViewHolder(@NonNull Myholder myholder, int i) {
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
}
@Override
public int getItemCount() {
return 99;
}
public class Myholder extends RecyclerView.ViewHolder {
EditText edJantriNumber;
TextView txtJantriNumber;
public Myholder(@NonNull View itemView) {
super(itemView);
edJantriNumber = itemView.findViewById(R.id.ed_jantri_number);
txtJantriNumber = itemView.findViewById(R.id.txt_jantri_number);
}
}
}
Here is the Activity
public class JantriActivity extends AppCompatActivity {
RecyclerView recyclerView;
SlideToActView slideToActView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jantri);
initiliaization();
recyclerView.setAdapter(new AdapterJantri());
}
private void initiliaization() {
recyclerView = findViewById(R.id.recycler_jantri);
recyclerView.setLayoutManager(new GridLayoutManager(this, 4));
slideToActView = findViewById(R.id.swipe_save_transaction);
}
}
Upvotes: 2
Views: 2444
Reputation: 18687
A ViewAdapter
re-uses a View
. So, when scrolling, instead of creating new views, an Adapter will re-use a view that is no longer visible.
So, that is the main reason you have the onBindViewHolder
. With that method, Android gives you chance to update the view content before it becomes visible. If you won't update all contents of the (the TextView
and the EditText
), you observe issues like that one that you mentioned.
Note this code:
@Override
public void onBindViewHolder(@NonNull Myholder myholder, int i) {
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
}
As you can see, you are only updating the TextView
. So, that TextView
(re-used or not) will always display proper text. However, you are not updating the content of the EditText
. This way, if the View was just created, the EditText
is empty. If the view is being re-used, you will see the old value (from a different line).
So, in order to keep update the EditText
properly, you must store all texts in a separated list. For example:
public class AdapterJantri extends RecyclerView.Adapter<AdapterJantri.Myholder> {
private String[] mTextList;
public AdapterJantri() {
mTextList = new String[getItemCount()];
}
@Override
public void onBindViewHolder(@NonNull Myholder myholder, int i) {
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
// Here, you update the EditText content
myholder.edJantriNumber.setText(mTextList[i]);
}
@Override
public void onViewRecycled(@NonNull final Myholder holder) {
// Here, you save the text before the view gets recycled
int index = holder.getAdapterPosition();
if (index >= 0) {
mTextList[index] = holder.edJantriNumber.getText().toString();
}
}
}
EDIT
If you have a TextWatcher
and you need to disable it temporarily, I suggest to:
First
Don't create a TextWatcher
inside the bindViewHolder
. You don't need to create a new one everytime. Just add it and remove it. You can use your own MyHolder
as a TextWatcher
public class Myholder extends RecyclerView.ViewHolder implements TextWatcher {
EditText edJantriNumber;
TextView txtJantriNumber;
public Myholder(@NonNull View itemView) {
super(itemView);
edJantriNumber = itemView.findViewById(R.id.ed_jantri_number);
txtJantriNumber = itemView.findViewById(R.id.txt_jantri_number);
}
@Override
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
// Add your code here
}
@Override
public void onTextChanged(final CharSequence s, final int start, final int before,
final int count) {
// Add your code here
}
@Override
public void afterTextChanged(final Editable s) {
// Add your code here
}
public void disableTextChangeListner() {
// This will remove the listener. So, it will stop to listen to new events
edJantriNumber.removeTextChangedListener(this);
}
public void addTextChangeListner() {
// This will add the listener. So, it will start to listen to new events
edJantriNumber.addTextChangedListener(this);
}
}
Then, during bindViewHolder
, disable the listener, set the new text, and add the listener again as foolows:
@Override
public void onBindViewHolder(@NonNull Myholder myholder, int i) {
myholder.disableTextChangeListner();
myholder.txtJantriNumber.setText(String.valueOf(i + 1));
myholder.edJantriNumber.setText(mTextList[i]);
myholder.addTextChangeListner();
}
Upvotes: 5