Saving EditText content in RecyclerView
Asked Answered
K

17

106

I have list item with EditText in it, I don't know how many items there will be. I have a problem when I enter some text in EditText, and then scroll down a RecyclerView, after I've scroll up again there is no text in my first EditText.

I am wondering what, and where should I write code so that while the user is typing or finished typing (I was thinking to do that with a TextWatcher) in the EditText the text gets saved into into a file (I'll save it in a .txt file in the external storage)

Am I supposed to do so in the onCreate method of the activity or in the adapter class or elsewhere?

Here is some code

Main Activity code

 public class MainActivity extends Activity {

    RecyclerView mRecyclerView;
    MyAdapter mAdapter;
    String[] mDataSet= new String[20];
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // generating text for editText Views
        for (int i = 0; i<=19; i++){
        mDataSet[i]= "EditText n: "+i;

    }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mAdapter = new MyAdapter(mDataSet);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setHasFixedSize(true);
    }

My adapter code

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

private String[] mDataset;


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

    public ViewHolder(View v) {
        super(v);

        mEditText = (EditText) v.findViewById(R.id.list_item_edittext);
    }
}

public MyAdapter(String[] myDataset) {
    mDataset = myDataset;
}

@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                 int viewType) {

    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.list_item, parent, false);

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

@Override
public void onBindViewHolder(ViewHolder holder,  final int position) {
    holder.mEditText.setText(mDataset[position]);

    //without this addtextChangedListener my code works fine ovbiusly
    // not saving the content of the edit Text when scrolled
    // If i add this code then when i scroll all textView that go of screen
    // and than come back in have messed up content
    holder.mEditText.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
           //setting data to array, when changed
           // this is a semplified example in the actual app i save the text
           // in  a .txt in the external storage
           mDataset[position] = s.toString();
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start,
                                      int count, int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });

}

@Override
public int getItemCount() {
    return mDataset.length;
}

without this "addtextChangedListener" my code works fine obviusly not saving the content of the edit Text when scrolled. If i add this code, when i scroll all editText views that go off screen and than come back in have messed up content.

Kacerek answered 5/8, 2015 at 23:18 Comment(8)
please share your code for adapter, layouts, activity/fragment that hosts recyclerView.Jud
Use a TextWatcher to save the entered text to an in-memory representation of your list, then save this to SharedPreferences in the Activity or Fragment onStop() method.Remediless
you can get that editText in your activity.You just have to find view of your editText of that position. If you want this type of code than I can help you.Tartarus
post your onBindViewHolderChanty
@dkarmazi, I've added the codeKacerek
@SwiftArchitect I've added the codeKacerek
@Chanty I've added the codeKacerek
you can you this adapter, it supports view states. github.com/vivchar/RendererRecyclerViewAdapterJost
J
198

The major problem with your solution is allocating and assigning TextWatcher in onBindViewHolder which is an expensive operation that will introduce lags during fast scrolls and it also seems to interfere with determining what position to update in mAdapter.

Making all operations in onCreateViewHolder is a more preferable option. Here is the complete tested working solution:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false);
        // pass MyCustomEditTextListener to viewholder in onCreateViewHolder
        // so that we don't have to do this expensive allocation in onBindViewHolder
        ViewHolder vh = new ViewHolder(v, new MyCustomEditTextListener());

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // update MyCustomEditTextListener every time we bind a new item
        // so that it knows what item in mDataset to update
        holder.myCustomEditTextListener.updatePosition(holder.getAdapterPosition());
        holder.mEditText.setText(mDataset[holder.getAdapterPosition()]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }


    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public EditText mEditText;
        public MyCustomEditTextListener myCustomEditTextListener;

        public ViewHolder(View v, MyCustomEditTextListener myCustomEditTextListener) {
            super(v);

            this.mEditText = (EditText) v.findViewById(R.id.editText);
            this.myCustomEditTextListener = myCustomEditTextListener;
            this.mEditText.addTextChangedListener(myCustomEditTextListener);
        }
    }

    // we make TextWatcher to be aware of the position it currently works with
    // this way, once a new item is attached in onBindViewHolder, it will
    // update current position MyCustomEditTextListener, reference to which is kept by ViewHolder
    private class MyCustomEditTextListener implements TextWatcher {
        private int position;

        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            // no op
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            mDataset[position] = charSequence.toString();
        }

        @Override
        public void afterTextChanged(Editable editable) {
            // no op
        }
    }
}
Jud answered 6/8, 2015 at 15:54 Comment(28)
Thank you! it works perfectly now! One more thing...do i have to make significant changes if i what to save it into external storage and not a String array ? Because i've tried to do it in the onTextChanged but i'm sure it's not the way it shoud be doneKacerek
You are the most welcome! Regarding your question, saving data to external storage (whether writing to disk or through network) is a very expensive operation and must be performed off the UI thread. Thus, you have to do that asynchronously and reduce the number of these calls to a minimum. Since you have 20 EditTextViews, making a separate assync call for each is even more expensive. What you can do is save locally to String[] just like you do it now. When you leave this screen, sync local String[] to your external storage. When you come back to this screen, pull String[] from external storageJud
Hi, this solution works well, but what if I have more than one edit text in each row of the recycler view?Effectual
Varun, in that case, you'd have to add a second editText to the layout, add it to the viewHolder, create a second MyCustomEditTextListener to listen to changes in the second editText object. Most important, you will need to create a new data structure identical to mDataset to remember values for the second editTexts.Jud
hmmm solution didn't work for me :( still erases edit text when I scroll down.Backed
@DougRay, post your code or link to it, it's hard to tell what goes wrong without seeing the code.Jud
@Jud Why no need call notifyItemChanged(index); when we update content of adapter ?Sylvestersylvia
@quangson91, I'm not sure if I completely understand the question. Could you please point to where specifically we update the content of adapter?Jud
@Jud I mean: ` @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { mDataset[position] = charSequence.toString(); }` you can see mDateset is changed value at position. So why we no need call notifyItemChangedSylvestersylvia
@Sylvestersylvia we don't have to call notifyItemChanged because it's an EditText view that we currently see on the screen. This view will display whatever was the last user's input while the view is visible. Now, mDataset becomes handy when the view goes out of the screen and then comes back. In case we had something like a regular TextView, then notifyItemChanged would be necessary to call to update the current displayed value of the view.Jud
Ok, So if content visible is same mDateset then we no need call notify to adapterSylvestersylvia
@Sylvestersylvia only in case you're dealing with EditText type of view, because you had literally typed the new content into this text box and then saved it to mDatesetJud
@Jud Ok, So for another case we must call notifyItemChange.Sylvestersylvia
It's work. but when i add texts in one of the edittext, the some other rows duplicates the same value. Is there any solution for this?Kukri
@ARUNBALANNV, this behavior should not happen and solution has been designed to address that very issue. Can you post your somewhere so that I can see what's going on?Jud
+1 Great answer, solved my issue. But I am facing an issue in this, when I scroll the RecyclerView the soft keyboard changed from numeric to alphanumeric and also the EditText looses focus.Chewink
UPDATE: should use holder.getAdapterPosition() instead of position in: holder.myCustomEditTextListener.updatePosition(position);Cognizable
As far as I can tell, this doesn't work at all. Given that onTextChanged is called when the EditText's holder gets recycled, it will just update the saved values to empty values, thus losing the dataStrage
Would it be easier if you could implement TextWatcher in the holder class and remove the static? That way you can encapsulate the callbacks in the holder.Governor
I agree with @JamEngulfer, this is not working because when the EditText is recycled as you are scrolling, it´s value is empty againJoellejoellen
@JesusAlmaral do you observe onTextChanged being invoked as you recycle?Jud
Yes, I am debugging and as I am scrolling, it´s being called multiple times when new rows appearJoellejoellen
gotcha, I didn't observe this back, but something must have changed. What's your device and api level?Jud
I feel obliged to share this post for people who are experiencing strange behaviour with this implementation.Poppas
@Poppas please feel free to share a sample project that has issues. I'll be happy to look at and debug it.Jud
This solution has a bug. One should never store position information passed to onBindViewHolder because the position of an item may change without another onBindViewHolder call being made. Instead MyCustomEditTextListener should be have a reference to a ViewHolder and call ViewHolder.getAdapterPosition() to obtain correct position.Madder
Hello Team, It's very strange if you add Header & Footer layout this is not working. I tried several way to solve this. I tried to solve this with Header & footer layout .Waggery
The edittext that is currently focused loses it's original cursor position and resets to start. If I use select selection with setText inside onBindViewHolder() it slows the EditTextCrooks
W
24

I had the same problem, I added the following line and it seems to have fixed the problem on my side.

mRecyclerview.setItemViewCacheSize(mDataset.size());

Hopefully this sorts out the issue on your side.

Weakminded answered 14/7, 2016 at 9:12 Comment(3)
This will work, but defeats the whole purpose of "recycling views" in a RecyclerView.Supervisory
perfect for small set of dataGneiss
Really helpful!! after so much of struggle finally done with this answer. (y)Tatary
G
16

I implemented @dkarmazi solution, but it didn't help me. So, I've come further and there's truly working solution.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                               int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false);
        // pass MyCustomEditTextListener to viewholder in onCreateViewHolder
        // so that we don't have to do this expensive allocation in onBindViewHolder
        ViewHolder vh = new ViewHolder(v, new MyCustomEditTextListener());

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // update MyCustomEditTextListener every time we bind a new item
        // so that it knows what item in mDataset to update
        holder.myCustomEditTextListener.updatePosition(holder.getAdapterPosition());
        holder.mEditText.setText(mDataset[holder.getAdapterPosition()]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }

    @Override
    public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
        ((ViewHolder) holder).enableTextWatcher();
    }

    @Override
    public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
        ((ViewHolder) holder).disableTextWatcher();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public EditText mEditText;
        public MyCustomEditTextListener myCustomEditTextListener;

        public ViewHolder(View v, MyCustomEditTextListener myCustomEditTextListener) {
            super(v);

            this.mEditText = (EditText) v.findViewById(R.id.editText);
            this.myCustomEditTextListener = myCustomEditTextListener;
        }
        
        void enableTextWatcher() {
            mEditText.addTextChangedListener(myCustomEditTextListener);
        }

        void disableTextWatcher() {
            mEditText.removeTextChangedListener(myCustomEditTextListener);
        }
    }

    // we make TextWatcher to be aware of the position it currently works with
    // this way, once a new item is attached in onBindViewHolder, it will
    // update current position MyCustomEditTextListener, reference to which is kept by ViewHolder
    private class MyCustomEditTextListener implements TextWatcher {
        private int position;

        public void updatePosition(int position) {
            this.position = position;
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            // no op
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
            mDataset[position] = charSequence.toString();
        }

        @Override
        public void afterTextChanged(Editable editable) {
            // no op
        }
    }
}

The main problem was that applied TextWatcher continued to work during item recycling.

I've tried to disable it before recycling, but there's no any "beforeRecycle" event methods. So I used onViewDetachedFromWindow method, and it has worked well.

Galina answered 7/1, 2019 at 17:24 Comment(1)
Best answer so far, this should be the accepted answer. Most upvoted answer has performance flaws, except thisEpstein
P
5

I would create an interface and pass the current adapter position to handle the text change event

    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false);
        ViewHolder vh = new ViewHolder(v, new ViewHolder.ITextWatcher() {
            @Override
            public void beforeTextChanged(int position, CharSequence s, int start, int count, int after) {
                // do something
            }

            @Override
            public void onTextChanged(int position, CharSequence s, int start, int before, int count) {
                mDataset[position] = s.toString();
            }

            @Override
            public void afterTextChanged(int position, Editable s) {
                // do something
            }
        });

        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        holder.mEditText.setText(mDataset[position]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }


    public static class ViewHolder extends RecyclerView.ViewHolder {

        public EditText mEditText;
        private ITextWatcher mTextWatcher;

        public interface ITextWatcher {
            // you can add/remove methods as you please, maybe you dont need this much
            void beforeTextChanged(int position, CharSequence s, int start, int count, int after);

            void onTextChanged(int position, CharSequence s, int start, int before, int count);

            void afterTextChanged(int position, Editable s);
        }

        public ViewHolder(View v, ITextWatcher textWatcher) {
            super(v);

            this.mEditText = (EditText) v.findViewById(R.id.editText);

            this.mTextWatcher = textWatcher;

            this.mEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    mTextWatcher.beforeTextChanged(getAdapterPosition(), s, start, count, after);
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    mTextWatcher.onTextChanged(getAdapterPosition(), s, start, before, count);
                }

                @Override
                public void afterTextChanged(Editable s) {
                    mTextWatcher.afterTextChanged(getAdapterPosition(), s);
                }
            });
        }
    }

}
Pomposity answered 23/2, 2016 at 16:38 Comment(1)
nicest and cleanest of all the answers. that's surely a way to goNaught
P
4

Create a String array with the size of your adapter data.

Eg: String[] texts = new String[dataSize];

on the onBindViewHolder method inside your adapter , add a TextChangedListener to the Textview.

Eg : -

@Override
    public void onBindViewHolder(Viewholder holder, int position) {

//binding data from array 
   holder.yourEditText.setText(texts [position]);
   holder.yourEditText.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start,
                    int before, int count) {
                //setting data to array, when changed
                texts [position] = s.toString();
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start,
                    int count, int after) {
                //blank
            }

            @Override
            public void afterTextChanged(Editable s) {
                //blank
            }
        });


}
Penile answered 6/8, 2015 at 3:53 Comment(1)
Hi @JITHINRAJ without this "addtextChangedListener" my code works fine obviusly not saving the content of the edit Text when scrolled. If i add this code, when i scroll all editText views that go off screen and than come back in have messed up content.Kacerek
C
2

Hi @mikwee make sure you are adding text changed listener in below method rather than adding it to onBindViewHolder().

public ViewHolder(View v) {
        super(v);

  yourEditText.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start,
                int before, int count) {
            //setting data to array, when changed
            texts [position] = s.toString();
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start,
                int count, int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });


}
Certitude answered 12/1, 2016 at 19:19 Comment(0)
E
2

According to me this is more optimize of @dkarmazi's answer

public class UploadPhotoAdapter extends RecyclerView.Adapter<UploadPhotoAdapter.MyViewHolder> {
        ArrayList<Feed> feeds;
        Activity activity;
        public UploadPhotoAdapter(Activity activity, ArrayList<Feed> feeds) {
            this.feeds = feeds;
            this.activity = activity;
        }

        @Override
        public UploadPhotoAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_feeds_recycler, parent, false);
            return new UploadPhotoAdapter.MyViewHolder(itemView);
        }

        @Override
        public void onBindViewHolder(final UploadPhotoAdapter.MyViewHolder holder, int position) {
            Feed feed = feeds.get(holder.getAdapterPosition());
            holder.captionEditText.setText(feed.getDescription());
            holder.captionEditText.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) {
                    feeds.get(holder.getAdapterPosition()).setDescription(s.toString());
                }
                @Override
                public void afterTextChanged(Editable s) {}
            });
        }
        @Override
        public int getItemCount() {
            return feeds.size();
        }

        public class MyViewHolder extends RecyclerView.ViewHolder {
            EditText captionEditText;
            public MyViewHolder(View view) {
                super(view);
                captionEditText = (EditText) view.findViewById(R.id.captionEditText);
            }
        }

    }
Energize answered 10/3, 2018 at 5:15 Comment(0)
H
2

Override onViewRecycled method in RecyclerView adapter like this:

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
    mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString();
}
Hairdresser answered 24/1, 2019 at 21:58 Comment(3)
Using holder.getAdapterPosition() inside the TextWatcher is the best solution - then you do not need to keep track of position, because the recycler is doing it for you. Just make sure that only one TextWatcher is added in the view holders constructor.Yellowthroat
@Yellowthroat My solution don't need TextWatcher. It's just override the onViewRecycled method of the RecyclerView.Adapter class.Hairdresser
@Hairdresser The problem with that is that you can edit the text and then press a save button (on the appBar) and onViewRecycled() will not be called, so you wont pickup the changes. With the TextWatcher you will always be notified of changes.Yellowthroat
S
2

The problem is that you 【add】 a listener , not 【set】 a listener. So when view is recycled, some EditText will have listeners more than 1.

To fix this problem, should clear listeners when view recycled.

Step 1

use custom EditText replace.

public class RecyclerViewEditText extends AppCompatEditText {

    private ArrayList<TextWatcher> mListeners = null;

    public RecyclerViewEditText(@NonNull Context context) {
        super(context);
    }

    public RecyclerViewEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RecyclerViewEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void addTextChangedListener(TextWatcher watcher)
    {
        if (mListeners == null)
        {
            mListeners = new ArrayList<>();
        }
        mListeners.add(watcher);

        super.addTextChangedListener(watcher);
    }

    @Override
    public void removeTextChangedListener(TextWatcher watcher)
    {
        if (mListeners != null)
        {
            int i = mListeners.indexOf(watcher);
            if (i >= 0)
            {
                mListeners.remove(i);
            }
        }

        super.removeTextChangedListener(watcher);
    }

    public void clearTextChangedListeners()
    {
        if(mListeners != null)
        {
            for(TextWatcher watcher : mListeners)
            {
                super.removeTextChangedListener(watcher);
            }

            mListeners.clear();
            mListeners = null;
        }
    }
}

step 2

replace EditText with above in xml.

    <com.xxxxxxxx.widget.RecyclerViewEditText
        android:id="@+id/et_remark"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:background="@null"
        android:gravity="end"
        android:hint="input hint"
        android:inputType="textPersonName" />

step 3

when view recycled, clear all listeners in your adapter.

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
    holder.et_remark.clearTextChangedListeners();
    super.onViewRecycled(holder);
}

see https://mcmap.net/q/205476/-how-to-remove-all-listeners-added-with-addtextchangedlistener

Sailor answered 29/12, 2020 at 6:55 Comment(0)
V
2

EditText#setText copies the string into an Editable which can be read by calling getText() right ahead. Thus, you can create an alternative editable data set:

fun onBindViewHolder(…) {
    holder.editText.setText(editables[position] ?: texts[position])
    editables[position] = holder.editText.text
}

Now editables will always hold actual data.

Here's a full sample that I wrote.

Virgy answered 9/7, 2023 at 0:5 Comment(0)
I
1

For me the above solutions didnt work. For some of them, the listener was not calling and when the listener was called in the onBindViewHolder method, it seems even when scrolling the listener events are called. 'Text' is changing, So i tried key listener and it worked fine. No keys are pressed during scrolling i guess.

holder.ticketNo.setOnKeyListener(new View.OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    results.get(position).TicketNo = holder.ticketNo.getText().toString();
                    return false;
                }
            });

The code that worked for me.

Ibrahim answered 22/8, 2019 at 16:53 Comment(0)
S
1

I overrided the method getItemViewType to resolved for me

override fun getItemViewType(position: Int): Int {
    return position
}
Standice answered 18/1, 2020 at 17:3 Comment(1)
Unfortunatelly that would create number of view holders per element and they will never be reused. In fact, the performance would be exactly the same as adding new elements inside LinearLayoutVerticillate
T
1

I think this is the easiest solution. Just setOnFocusChangeListener to your EditText and add/remove TextWatcher accordingly:

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    // Initialize the TextWatcher when the ViewHolder is bound.
    // You need to make sure you don't add a TextWatcher to an EditText that already has one.

    holder.editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            EditText editText = (EditText) v;

            if (hasFocus) {
                // Add the TextWatcher when the EditText has focus
                TextWatcher textWatcher = 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) {
                        // ...
                    }
                };
                
                editText.addTextChangedListener(textWatcher);
                editText.setTag(textWatcher);  // Store the TextWatcher as a tag so you can remove it later.
            } else {
                // Remove the TextWatcher when the EditText loses focus
                TextWatcher textWatcher = (TextWatcher) editText.getTag();
                if (textWatcher != null) {
                    editText.removeTextChangedListener(textWatcher);
                }
            }
        }
    });

}
Tyranny answered 26/7, 2023 at 14:20 Comment(1)
Simple & best solution. Only thing is, use this in onCreateViewHolder(). That is the recommended way to avoid anti pattern.Lakenyalaker
W
0

I'm not that familiar with RecyclerView objects, but I had the same issue with ListView. For those ones, I usually create an ad-hoc class representing values inserted into my views (it works with EditTexts, Checkboxes, RadioButtons...) and get updated data through them. I then create a custom ArrayAdapter consisting of said container objects, retrieving values to put into the edittexts at every getView() callback from them and implementing a textwatcher to keep these objects up to date. Again, I don't exactly remember how RecyclerViews work, but if they involve Adapters, this could be a good hack for you to try.

Wersh answered 6/8, 2015 at 0:3 Comment(0)
P
0

use Two-way binding if working with databinding

https://medium.com/swlh/how-data-binding-helps-you-when-working-with-edittext-inside-recyclerview-543a1eb5f2cc

android:text="@={model.name}"

Petta answered 20/6, 2020 at 10:6 Comment(0)
K
0

implements View.OnFocusChangeListener

@Override
public void onBindViewHolder(Viewholder holder, int position) {
        editText.setText(model.getValue());
        editText.setOnFocusChangeListener(this);
        editText.setTag(position);
}

@Override
public void onFocusChange(View v, boolean hasFocus) {
    if (v.getTag() == null)
        return;
    int position = (int) v.getTag();
    if (!hasFocus && v instanceof EditText)
        mValues.get(position).setValue(((EditText) v).getText().toString());
}
Karmakarmadharaya answered 14/7, 2020 at 20:18 Comment(0)
D
0

I used the tag property to save past watcher and be able to remove it:

 editText.tag?.let {
      editText.removeTextChangedListener(it as TextWatcher)
 }

object:TextWatcherAdapter() {
       override fun afterTextChanged(s: Editable) {
               // do something with text
       }
}.also { 
       editText.addTextChangedListener(it)
       editText.tag = it
}
            
Daveen answered 28/11, 2022 at 22:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.