I'm using EditText
where I want to preset text or let user type something to it. I wanted to make it "all-caps" so I implemented capitalize function for my TextWatcher
.
You can enter and leave screen with this EditText
and TextWatcher
multiple times go back and forth switching screens - that means I have to properly register and unregister TextWatcher
from EditText
upon screen enter/leave event.
What I noticed is, that if I enter and leave screen multiple times in a row, upon like 5th enter my whole app is frozen - so something is blocking UI Thread.
On deeper investigation I found out that if I call editText.setText("some string")
, it will trigger my afterTextChanged
listener multiple times. On 5th enter it triggered it like 50 times in a row and that caused UI Thread block.
How is this fixable or is it bug on Android side?
Code: Initializing screen on enter:
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)}
}
All-Cap transformation function (should not trigger 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)
}
Clearing screen before leaving:
formFirstEt?.setText("")
inputWatcher?.let { formFirstEt?.removeTextChangedListener(it) }
inputWatcher = null
Logs:
-- 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
UPDATE:
This is some Kotlin behaviour. Problem was with formFirstEt?.setText("")
I expected it will add empty String to EditText
, but apperently it is adding character terminator. Kotlin cant recognize that its String
but its char array
by default with \0
character at the end. This char array is triggering some weird behaviour inside native setText
function which is spamming afterTextChanged
Fix:
//type declaration String is important without it it will not work
val emptyString: String = ""
formFirstEt?.setText(emptyString)