Shekhar Saini
Shekhar Saini

Reputation: 99

EditText losing its content on RecyclerView. How to restore its content?

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

Answers (1)

guipivoto
guipivoto

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

Related Questions