I also bumped into this situation when had a series of fields containing 1 symbol (a usual pin-code).
My solution is based on @jmeinke answer.
Create a layout containing several EditText
s with android:maxLength="2"
(we will enter 1 symbol in any of them and jump to another EditText
. Strange, but on some phones the code below works even for maxLength=1).
class YourTextWatcher(
private val prevEdit: EditText?,
private val currEdit: EditText,
private val nextEdit: EditText?,
private val method: () -> Unit
) : TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (s.isNullOrEmpty()) {
prevEdit?.requestFocus()
} else {
if (s.length > 1) {
if (currEdit.selectionEnd > 1) {
// If stand on second position of EditText and enter new symbol,
// will move to next EditText copying second symbol.
val secondSymbol = s.substring(1, 2)
nextEdit?.setText(secondSymbol)
}
// Remove second symbol.
s.delete(1, s.length)
}
nextEdit?.requestFocus()
nextEdit?.setSelection(nextEdit.length(), nextEdit.length())
method()
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
}
Then for all EditText
s do following:
currEdit.addTextChangedListener(
YourTextWatcher(prevEdit, currEdit, nextEdit) { yourMethodToValidateText() })
Instead of prevEdit, currEdit, nextEdit set your EditText
controls. For instance, for the first:
edit1.addTextChangedListener(
YourTextWatcher(null, edit1, edit2) { yourMethodToValidateText() })
yourMethodToValidateText() is a method that will check entered symbols. For instance, you can check if all EditText
s are filled.
This solution has a bug when you try to press BackSpace in empty EditText
. Nothing happens, you won't go to a previous EditText
. You can catch BackSpace in View.OnKeyListener, but it can intersect with TextWatcher. Also I didn't check a situation when copy & paste a text to EditText
, forgot to check, what happens, when select a text in EditText
and press Delete.