Textwatcher для моего EditText запускает afterTextChanged несколько раз

#android #android-edittext #android-textwatcher

#Android #android-edittext #android-textwatcher

Вопрос:

Я использую EditText там, где я хочу задать текст или позволить пользователю вводить что-то в него. Я хотел сделать его «all-caps», поэтому я реализовал функцию capitalize для my TextWatcher .

Вы можете входить и выходить из экрана с помощью этого EditText и TextWatcher несколько раз переходить туда и обратно, переключая экраны — это означает, что я должен правильно зарегистрироваться и отменить TextWatcher EditText регистрацию после события ввода / вывода экрана.

Я заметил, что если я вхожу и покидаю экран несколько раз подряд, после 5-го ввода все мое приложение зависает — значит, что-то блокирует поток пользовательского интерфейса.

При более глубоком исследовании я обнаружил, что если я позвоню editText.setText("some string") , это вызовет моего afterTextChanged слушателя несколько раз. При 5-м вводе он запускал его примерно 50 раз подряд, что вызывало блокировку потока пользовательского интерфейса.

Как это можно исправить или это ошибка на стороне Android?

Код: инициализация экрана при вводе:

 initEditTextListener()
App.log("FormScreen - setData")
formFirstEt?.setText(item.name)

private var inputWatcher: TextWatcher? = null
private fun initEditTextListener(){
        formFirstEt?.onEditorAction{
            formInputValidation(this.text.toString(), formFirstEt, App.getString("form_empty"), App.getString("form_empty_reason"))
            validateData()
            formSecondEt?.let { s -> if (s.isEnabled) s.requestFocus() }
        }

        inputWatcher = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                App.log("FormScreen - initEditTextListener - afterTextChanged")
                validateData()
            }
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                App.log("FormScreen - initEditTextListener - onTextChanged")
                validateData()
                formFirstEt?.let { et -> onFormFirstInputChanged(s.toString(), this, et)}
            }
        }

        inputWatcher?.let {formFirstEt?.addTextChangedListener(it)}
    }
 

Функция преобразования всех заголовков (не должна запускать TextWatcher)

 fun onFormFirstInputChanged(s: String, tv: TextWatcher, et: EditText) {
    et.removeTextChangedListener(tv)
    if (isFirstInputValid(s)) {
        with(et) {
            text.clear()
            append(s.toUpperCase(Locale.getDefault()))
            setSelection(s.length)
        }
    } else {
        val substring = s.substring(0, s.length - 1)
        with(et) {
            text.clear()
            append(substring.toUpperCase(Locale.getDefault()))
            setSelection(substring.length)
        }
    }
    et.addTextChangedListener(tv)
}
 

Очистка экрана перед выходом:

 formFirstEt?.setText("")
inputWatcher?.let { formFirstEt?.removeTextChangedListener(it) }
inputWatcher = null
 

Журналы:

 -- First time screen enter ---
2021-01-14 11:24:50.324 1136-1136/? I/Project: FormScreen - setData
2021-01-14 11:24:50.458 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:50.460 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
2021-01-14 11:24:52.838 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:52.841 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged

-- Second time screen enter --
2021-01-14 11:24:56.324 1136-1136/? I/Project: FormScreen - setData
2021-01-14 11:24:56.366 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:56.367 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:56.368 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
2021-01-14 11:24:56.368 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:56.369 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
2021-01-14 11:24:56.369 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:56.370 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:56.370 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
2021-01-14 11:24:56.371 1136-1136/? I/Project: FormScreen - initEditTextListener - onTextChanged
2021-01-14 11:24:56.372 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
2021-01-14 11:24:56.372 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
2021-01-14 11:24:56.373 1136-1136/? I/Project: FormScreen - initEditTextListener - afterTextChanged
 

ОБНОВЛЕНИЕ:
Это какое-то поведение Kotlin. Проблема была в formFirstEt?.setText("") том, что я ожидал, что он добавит пустую строку EditText , но, по-видимому, он добавляет символ-терминатор. Kotlin не может распознать, что String это, но char array по умолчанию с символом в конце. Этот массив символов вызывает какое-то странное поведение внутри встроенной setText функции, которая рассылает спам afterTextChanged

Исправить:

 //type declaration String is important without it it will not work
val emptyString: String = ""
formFirstEt?.setText(emptyString)
 

Комментарии:

1. У меня было что-то подобное, но не уверен, что это ваш случай. Мое решение состояло в том, чтобы зарегистрировать слушателя только onResume

2. Упомянутое исправление. Странное поведение 🙂

3. @Bek Потому что ничто не помешает пользователю нажать Shift на клавиатуре и отключить функцию capslock.

Ответ №1:

Здесь та же проблема. У меня есть DialogFragment , где я использую a TextInputEditText с наблюдателем. Если я addTextChangedListener в onCreateView (обычно ожидается при использовании a DialogFragment ), то слушатель срабатывает либо один, либо несколько раз за нажатие клавиши (случайным образом).

Даже использование локальной переменной watcherEnabled для обеспечения того, чтобы форматирование введенного значения не приводило к повторному запуску наблюдателя. сбой.

Вместо этого попытался переместить назначение наблюдателя onResume . Это кажется совершенно нелепым и неправильным, но, похоже, решило проблему.

Также обратите внимание, что средство просмотра текста запускается несколько раз только при использовании компьютерной клавиатуры для ввода текста в эмулятор. Когда я использую клавиатуру эмулятора, наблюдатель, похоже, не дает сбоев. Это МОЖЕТ указывать на то, что ошибка связана исключительно с эмулятором.