Как удалить всех слушателей, добавленных с помощью addTextChangedListener

У меня есть ListView где каждый ряд имеет EditText контроль. Я хочу добавить TextChangedListener к каждому ряду; тот, который содержит дополнительные данные, которые говорят, какая строка EditText был в. Проблема в том, что как getView вызывается, несколько TextWatchers добавлены; поскольку convertView уже имея TextWatcher (и тот, который указывает на другую строку).

MyTextWatcher watcher = new MyTextWatcher(currentQuestion);
EditText text = (EditText)convertView.findViewById(R.id.responseText);
text.addTextChangedListener(watcher);

MyTextWatcher мой класс, который реализует TextWatcher; и обрабатывает текстовые события. CurrentQuestion позволяет мне узнать, по какой строке я действую. Когда я печатаю в поле; несколько случаев TextWatcher называются.

Есть ли способ удалить TextWatchers перед добавлением нового? Я вижу removeTextChangedListener метод, но это требует определенного TextWatcher чтобы передать, и я не знаю, как получить указатель на TextWatcher это уже там.

15 ответов

Там нет никакого способа сделать это с помощью текущего EditText интерфейс напрямую. Я вижу два возможных решения:

  1. Редизайн приложения, чтобы вы всегда знали, что TextWatcher добавляются в частности EditText пример.
  2. простираться EditText и добавить возможность очистить всех наблюдателей.

Вот пример второго подхода - 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;
        }
    }
}

А вот как вы можете использовать ExtendedEditText в макетах XML:

<?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>

Вы можете удалить TextWatcher из вашего EditText. Прежде всего, я предлагаю вам переместить объявление TextWatcher за пределы editText.addTextChangedListener(...):

protected TextWatcher yourTextWatcher = new TextWatcher() {

    @Override
    public void afterTextChanged(Editable s) {
        // your logic here
    }

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

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
       // your logic here
    }
};

После этого вы сможете установить TextWather немного проще:

editText.addTextChangedListener(yourTextWatcher);

Чем вы можете удалить TextWatcher следующим образом:

editText.removeTextChangedListener(yourTextWatcher);

и установите другой, если хотите.

Я также потратил много времени на поиск решения и, наконец, решил с помощью тега, как показано ниже. Он удалит предыдущие экземпляры TextWatcher, получив ссылки из тега convertView. Это прекрасно решает проблему. В вашем файле CustomAdapter установите новый внутренний класс, как показано ниже:

private static class ViewHolder {

        private TextChangedListener textChangedListener;
        private EditText productQuantity;

        public EditText getProductQuantity() {
            return productQuantity;
        }    

        public TextChangedListener getTextChangedListener() {
            return textChangedListener;
        }

        public void setTextChangedListener(TextChangedListener textChangedListener) {
            this.textChangedListener = textChangedListener;
        }
    }

Затем в вашем переопределенном общедоступном методе View getView (int position, View convertView, ViewGroup parent) реализуйте логику, подобную приведенной ниже:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

     EditText productQuantity;
    TextChangedListener textChangedListener;

    if(convertView==null) {
        LayoutInflater mInflater = (LayoutInflater)
                context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        convertView = mInflater.inflate(R.layout.cart_offer_item, parent, false);

        productQuantity=(EditText)convertView.findViewById(R.id.productQuantity);
        addTextChangedListener(viewHolder, position);
        convertView.setTag(viewHolder);
    }
    else
    {
        ViewHolder viewHolder=(ViewHolder)convertView.getTag();
        productQuantity=viewHolder.getProductQuantity();
        removeTextChangedListener(viewHolder);
        addTextChangedListener(viewHolder, position);
    }

    return convertView;
}



private void removeTextChangedListener(ViewHolder viewHolder)
{
    TextChangedListener textChangedListener=viewHolder.getTextChangedListener();
    EditText productQuantity=viewHolder.getProductQuantity();
    productQuantity.removeTextChangedListener(textChangedListener);
}

private void addTextChangedListener(ViewHolder viewHolder, int position)
{
    TextChangedListener textChangedListener=new TextChangedListener(position);
    EditText productQuantity=viewHolder.getProductQuantity();
    productQuantity.addTextChangedListener(textChangedListener);
    viewHolder.setTextChangedListener(textChangedListener);
}

Затем реализуйте класс TextWatcher, как показано ниже:

private class TextChangedListener implements TextWatcher
{
    private int position;
    TextChangedListener(int position)
    {
        this.position=position;
    }
    @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) {
    Log.d("check", "text changed in EditText");

    }
}

Он удалит предыдущие экземпляры TextWatcher, получив ссылки из тега convertView

Давно этот вопрос не задавался, но может кому пригодится. Проблема с TextWatcher в Recyclerview заключается в том, что мы должны убедиться, что он удален до того, как представление будет переработано. В противном случае мы потеряем экземпляр TextWatcher, а вызов removeTextChangedListener(textWatcher) в OnBindViewHolder() удалит только текущий экземпляр TextWatcher.

Я решаю эту проблему, добавляя TextChangedListener внутрь FocusChangedListener:

      editText.setOnFocusChangeListener(new OnFocusChangeListener() {          
public void onFocusChange(View v, boolean hasFocus) {
    if(hasFocus) {
        editText.addTextChangedListener(textWatcher)
    }
    else{
        editText.removeTextChangedListener(textWatcher)
    }
  }
});

Таким образом, я уверен, что когда editText не имеет фокуса, текстовый наблюдатель удаляется и снова добавляется, когда он имеет фокус. Таким образом, когда recyclerview перерабатывается, в editText будет удален любой textChangeListener.

Сохраните текущий текстовый наблюдатель в окне просмотра, и вы сможете найти тот, который хотите удалить.

Я боролся с аналогичной проблемой с большим количеством EditTexts в RecyclerView. Я решил это размышлением. Вызов ReflectionTextWatcher.removeAll(your_edittext)перед связыванием просмотров. Этот фрагмент кода находит все TextWatcher и удаляет их из локального списка EditText, который называется "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;
    }
}

Надеюсь, это кому-то поможет.

Если кто-то, как я, имеет дело с ViewHolder, то простое сохранение ссылки на наблюдатель текста при его создании не поможет. При повторном использовании представление перейдет к другомуViewHolder который не будет иметь ссылки на этот старый наблюдатель текста, поэтому его нельзя будет удалить.

Лично я решил решить такую ​​проблему, как @inazaruk, хотя обновленный код на Kotlin + переименовал класс, чтобы лучше отразить его цель.

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()
    }
}

Я разрешил эту ситуацию без расширения класса TextView.

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();
}

Как вы можете видеть здесь: CodeSearch TextView нет способа удалить всех слушателей. Единственный способ - предоставить наблюдателя, который вы использовали для его регистрации.

Я еще не до конца понимаю, почему уже зарегистрированы другие слушатели. Однако вы можете создать подкласс EditText, переопределить addTextChangedListener (..) и сохранить в нем копию всех добавленных ссылок самостоятельно, а затем делегировать реализацию суперкласса. Затем вы можете также предоставить дополнительный метод, который удаляет всех слушателей.

Свяжитесь с нами, если вам нужны дальнейшие объяснения.

Я столкнулся с проблемой при использовании in inRecyclerViewэлемент, и это вызывало ошибку бесконечного цикла, когдаViewHolderбыла привязка, потому что был вызван добавленный в предыдущем вызове привязки, следовательно, бесконечный цикл..

Единственным рабочим решением для этого было хранить 's в списке, а затем вonBindViewHolder, просмотрите этот список и удалите из .

      private val textWatchers: MutableList<TextWatcher> = mutableListOf()

Добавить в список, прежде чем назначать его:

      textWatchers.add(textWatcher1)
vh.moneyAmount.editText?.addTextChangedListener(textWatcher1)

Удалите их при привязке изделия, перебирая всюtextWatcherList:

      private fun removeTextWatcher(vh: MoneyItemViewHolder) {
    textWatchers.forEach { vh.moneyAmount.editText?.removeTextChangedListener(it) }
}

Другого способа удалить 's изEditText, чем прохождениеTextWatcherобъект, поэтому его нужно где-то хранить, если мы планируем удалить его позже.

Я боролся с подобной проблемой. Я решил это, сохранив ссылки на мои textWatchers в 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));
}

У меня была такая же проблема с xamarin/C#, и я написал для этого класс для управления событиями щелчка внутри ListView, где представление элемента будет "переработано":

 public class ViewOnClickEventHandler: Java.Lang.Object
 {
    private List<EventHandler> EventList { get; set; }

    public void SetOnClickEventHandler(View view, EventHandler eventHandler)
    {
        if (view.Tag != null)
        {
            ViewOnClickEventHandler holder = ((ViewOnClickEventHandler)view.Tag);

            foreach (EventHandler evH in holder.EventList)
                view.Click -= evH;

            for (int i = 0; i < holder.EventList.Count; i++)
                holder.EventList[i] = null;

            holder.EventList.Clear();
        }

        EventList = new List<EventHandler>();
        EventList.Add(eventHandler);
        view.Click += eventHandler;
        view.Tag = this;
    }
}

Вы можете использовать его в вашем методе ListView BaseAdapter GetItem следующим образом:

       TextView myTextView = convertView.FindViewById<TextView>(Resource.Id.myTextView);

        ViewOnClickEventHandler onClick = new ViewOnClickEventHandler();
        onClick.SetOnClickEventHandler(myTextView, new EventHandler(delegate (object sender, EventArgs e)
        {
            // Do whatever you want with the click event
        }));

Класс ViewOnClickEventHandler будет заботиться о нескольких событиях в вашем текстовом представлении. Вы также можете изменить класс для событий textchange. Это тот же принцип. Я надеюсь, это поможет.

пока, nxexo007

То, что я сделал для удаления наблюдателей текста, очень просто. Я создал массив для размещения моих текстовых наблюдателей:

final TextWatcher[] textWatchers = new TextWatcher[3];

Я добавил их в:

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);

Мой метод returnTextWatcherCheckPIN создает экземпляр textWatcher с другим средством проверки (switchMethod для проверки всех четырех editTexts) в afterTextChanged.

Затем, когда я удаляю средство просмотра текста, я просто ссылаюсь на него из массива:

etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);

Проверьте размер слушателей editText на отладке:

введите описание изображения здесь

Это удалено! Это решило мою проблему!

Почему бы не прикрепить ссылку textwatcher к самому тексту редактирования с помощью setTag()

                          if(etTagValue.getTag(R.id.textWatcherTag)!=null){
                        etTagValue.removeTextChangedListener((TextWatcher) etTagValue.getTag());
                    }
                    etTagValue.setText(myValue);
                    TextWatcher textWatcher = new DelayedTextWatcher(text -> meta.setDescription(text.toString()));
                    etTagValue.addTextChangedListener(textWatcher);
                    etTagValue.setTag(R.id.textWatcherTag,textWatcher);

В ids.xml в разделе /values

      <?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="textWatcherTag" type="id" />
</resources>

Поскольку я один из тех, кто сталкивается с множественными проблемами прослушивателя EditText doOnTextChanged в адаптере при использовании notifiDataSetChange. После стольких исследований и попыток множества способов я нашел решение здесь, в разделе ответов, предоставленном @Davi. И теперь я создал встроенную функцию Kotlin для более простого использования, в дополнение к ответу г-на @Davi. Надеюсь, это поможет вам, ребята

      inline fun EditText.doAfterTextChangedMultipleListenerFix(
   crossinline afterTextChanged: (text: Editable?) -> Unit
) {

   val textWatcher = object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
        afterTextChanged.invoke(s)
    }

    override fun beforeTextChanged(text: CharSequence?, start: Int, 
        count: Int, after: Int) {
    }

    override fun onTextChanged(text: CharSequence?, start: Int, before: 
      Int, count: Int) {
    }
}

onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
    if (hasFocus) {
        addTextChangedListener(textWatcher)
    } else {
        removeTextChangedListener(textWatcher)
    }
 }

}

Вот как вы используете

      youEditText.doAfterTextChangedMultipleListenerFix{text->
    //do what you want
}

И, наконец, вы должны очистить фокус, прежде чем уведомлять об изменении адаптера.

Другие вопросы по тегам