Как изменить текст TextView в DataChange без обратного вызова слушателя TextWatcher

Учитывать:

TextView textView = new TextView(context);
    textView.addTextChangedListener(new TextWatcher() {

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

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

        @Override
        public void afterTextChanged(Editable s) {

            s.append("A");
        }
    });

Если мы добавим TextWatcher к TextView, и я хочу добавить букву к этому TextView, каждый раз, когда пользователь пишет в нем букву, но это продолжает вызывать TextWatcher Listener, и так до StackOverFlow error, так как я могу добавить текст без снова вызвать TextWatcher Listener?


person Mohammad Ersan    schedule 15.06.2011    source источник
comment
TypeMismatch: невозможно преобразовать из String в Editable   -  person IgorGanapolsky    schedule 17.04.2012


Ответы (5)


В документации afterTextChanged говорится:

Этот метод вызывается, чтобы уведомить вас, что где-то внутри s текст был изменен. Можно вносить дальнейшие изменения в s из этого обратного вызова, но будьте осторожны, чтобы не попасть в бесконечный цикл, потому что любые сделанные вами изменения приведут к повторному рекурсивному вызову этого метода. (Вам не сообщается, где произошло изменение, потому что другие методы afterTextChanged() могли уже внести другие изменения и сделать смещения недействительными. Но если вам нужно знать здесь, вы можете использовать setSpan(Object, int, int, int) в onTextChanged(CharSequence, int, int, int), чтобы отметить свое место, а затем искать от здесь, где закончился диапазон.

Итак, с каждым s.append("A") вы снова call afterTextChanged() и так далее.

person iDroid    schedule 15.06.2011
comment
Я не понимаю, почему setSpan помогает избежать рекурсии. - person Lance Kind; 28.06.2021

Это просто:

@Override
public void afterTextChanged(Editable s) {
    editText.removeTextChangedListener(this);
    //Any modifications at this point will not be detected by TextWatcher,
    //so no more StackOverflowError 
    s.append("A");
    editText.addTextChangedListener(this);
}
person Andrii Chernenko    schedule 08.06.2013
comment
Сохранение ссылки на editText внутри TextWatcher вызовет утечку памяти, убедитесь, что вы установили для него значение null, когда закончите с editText (например, активность хоста при уничтожении). - person AliLotfi; 05.02.2018
comment
впечатляющий. Мыслить творчески. - person Ivan; 05.07.2019

Другой способ избежать переполнения стека:

TextView textView = new TextView(context);
    textView.addTextChangedListener(new TextWatcher() {

        boolean editing = false;

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

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

        }

        @Override
        public void afterTextChanged(Editable s) {
            if (!editing){
               editing = true;
               s.append("A");
               editing = false;
        }

    }
});
person Iurii Vasylenko    schedule 04.03.2015
comment
Отлично. Менее ресурсоемкий, чем другие ответы. - person stevehs17; 20.03.2016
comment
Курсор возвращается в исходное положение - person Shshank Bhong; 20.09.2017
comment
Ключевой момент: установите логическое значение ПЕРЕД добавлением, иначе вы не получите свой логический набор до того, как будет запущено другое событие изменения текста. - person Lance Kind; 28.06.2021
comment
Объяснение было бы в порядке. В чем суть? Использование флага? Это сразу же поднимает вопрос, является ли он потокобезопасным или реентерабельным. Пожалуйста, ответьте, отредактировав (изменив) свой ответ, а не здесь, в комментариях (без Изменить:, Обновить: и т. п. — ответ должен выглядеть так, как будто он был написан сегодня). - person Peter Mortensen; 19.07.2021

Некоторый псевдокод, чтобы вы могли сделать это:

Просто смените фокус...

Ну вот так:

tv.isFocusable = false

tv.setText("my new text")

tv.isFocusable = true // Maybe post this to the message queue, so other jobs finish fist.


// Later on in your listener:

if(tv.isFocusable && tv.hasFocus())
    // Do something
else ignore
person j2emanue    schedule 08.06.2021

Котлин Версия

editText.addTextChangedListener(object: TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        if (s.toString().isNotBlank()) {

            val formattedValue: String = // Do some formatting

            editText.removeTextChangedListener(this)
            editText.setText(formattedValue)
            editText.setSelection(editText.text.toString().length)
            editText.addTextChangedListener(this)
        }
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }

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

    }

})
person Kavin Varnan    schedule 01.11.2017
comment
Объяснение было бы в порядке. Например, версия Kotlin чего? В чем суть? Пожалуйста, ответьте, отредактировав (изменив) свой ответ, а не здесь, в комментариях (без Изменить:, Обновить: и т. п. — ответ должен выглядеть так, как будто он был написан сегодня). - person Peter Mortensen; 19.07.2021