I have a ListView where each row has an EditText control. I want to add a TextChangedListener to each row; one that contains extra dat
I struggled with a similar problem. I solved it by saving references to my textWatchers in an ArrayList:
private final List<TextWatcher> textWatchersForProfileNameTextBox = new ArrayList<>();
public void addTextWatcherToProfileNameTextBox(TextWatcher textWatcher){
textWatchersForProfileNameTextBox.add(textWatcher);
getProfileNameTextView().addTextChangedListener(textWatcher);
}
public void removeAllTextWatchersFromProfileNameTextView(){
while (!textWatchersForProfileNameTextBox.isEmpty())
getProfileNameTextView().removeTextChangedListener(textWatchersForProfileNameTextBox.remove(0));
}
What I did to remove text watchers is very simple. I created an array to put my textwatchers:
final TextWatcher[] textWatchers = new TextWatcher[3];
I added them in:
final int CURRENT_PIN_CHECK = 0, NEW_PIN = 1, CONFIRM_PIN_CHECK = 2;
textWatchers[CURRENT_PIN_CHECK] = returnTextWatcherCheckPIN(CURRENT_PIN_CHECK);
textWatchers[NEW_PIN] = returnTextWatcherCheckPIN(NEW_PIN);
textWatchers[CONFIRM_PIN_CHECK] = returnTextWatcherCheckPIN(CONFIRM_PIN_CHECK);
My returnTextWatcherCheckPIN method instantiates a textWatcher with a different checker (switchMethod to check all four editTexts) on afterTextChanged.
Then whenever I remove a text watcher I just referenced the one from the array:
etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);
Check the listeners size of the editText on debug:
It's removed! That solved my problem!
If one, like me, deals with ViewHolder, then simply saving a reference to a text watcher upon its creation will not help. Upon reuse the view will get to some other ViewHolder which would not have a reference to that old text watcher, thus one won't be able to delete it.
Personally i chose to solve problem like @inazaruk, though updated code to Kotlin + renamed class to better reflect it's purpose.
class EditTextWithRemovableTextWatchers(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs) {
private val listeners by lazy { mutableListOf<TextWatcher>() }
override fun addTextChangedListener(watcher: TextWatcher) {
listeners.add(watcher)
super.addTextChangedListener(watcher)
}
override fun removeTextChangedListener(watcher: TextWatcher) {
listeners.remove(watcher)
super.removeTextChangedListener(watcher)
}
fun clearTextChangedListeners() {
for (watcher in listeners) super.removeTextChangedListener(watcher)
listeners.clear()
}
}
I resolved this situation without extend TextView class.
private ArrayList<TextWatcher> mEditTextWatcherList = new ArrayList<>();
private TextWatcher mTextWatcher1;
private TextWathcer mTextWatcher2;
mTextWathcer1 = 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) {}
};
mTextWathcer2 = 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) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
setListener(mTextWatcher1);
setListener(mTextWatcher2);
removeListeners();
}
private setListener(TextWatcher listener) {
mEditText.addTextChangedListener(listener);
mEditTextWatcherList.add(listener);
}
private removeListeners() {
for (TextWatcher t : mEditTextWatcherList)
mEditText.removeTextChangedListener(t);
mEditTextWatcherList.clear();
}
I struggled with a similar problem with a lot of EditTexts in RecyclerView. I solved it by reflection. Call ReflectionTextWatcher.removeAll(your_edittext) before bind views. This piece of code finds all TextWatchers and removes them from the local EditText's list called "mListeners".
public class ReflectionTextWatcher {
public static void removeAll(EditText editText) {
try {
Field field = findField("mListeners", editText.getClass());
if (field != null) {
field.setAccessible(true);
ArrayList<TextWatcher> list = (ArrayList<TextWatcher>) field.get(editText); //IllegalAccessException
if (list != null) {
list.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static Field findField(String name, Class<?> type) {
for (Field declaredField : type.getDeclaredFields()) {
if (declaredField.getName().equals(name)) {
return declaredField;
}
}
if (type.getSuperclass() != null) {
return findField(name, type.getSuperclass());
}
return null;
}
}
I hope, this will help someone.
There is no way to do this using current EditText interface directly. I see two possible solutions:
TextWatcher are added to particular EditText instance.EditText and add possibility to clear all watchers.Here is an example of second approach - ExtendedEditText:
public class ExtendedEditText extends EditText
{
private ArrayList<TextWatcher> mListeners = null;
public ExtendedEditText(Context ctx)
{
super(ctx);
}
public ExtendedEditText(Context ctx, AttributeSet attrs)
{
super(ctx, attrs);
}
public ExtendedEditText(Context ctx, AttributeSet attrs, int defStyle)
{
super(ctx, attrs, defStyle);
}
@Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<TextWatcher>();
}
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;
}
}
}
And here is how you can use ExtendedEditText in xml layouts:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ua.inazaruk.HelloWorld.ExtendedEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="header"
android:gravity="center" />
</LinearLayout>