Kotlin edit text ; textwatcher for phoneNumber

432 Views Asked by At

i am trying to get a phone number from editText in my project. the problem i'm having;

1 -> the first digit should not be 0. 2 -> unfortunately but I can't delete spaces..

The number format I want to get; (555) 555 55 55 The stage my code has come to;

class PhoneNumberMask(val editText: EditText) : TextWatcher {
    var phoneNumber: String = ""
    var isRunning: Boolean = false

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

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

    override fun afterTextChanged(s: Editable) {
        if (isRunning || s.length == 15) {
            return
        }

        isRunning = true
        phoneNumber = when (s.length) {
            0 -> "test"
            1 -> "($s"
            4 -> "$s) "
            9 -> "$s "
            12 -> "$s "
            else -> s.toString()
        }

        editText.setText(phoneNumber).also { editText.setSelection(phoneNumber.length) }
        isRunning = false
    }
}
1

There are 1 best solutions below

5
Tenfour04 On

I think your strategy is going to have troubles when the user moves the cursor to the middle of the text field, backspaces, or tries to paste text. What I would try is filtering to only digits each time it's changed and insert parentheses/spaces for the whole string of digits every time. Then put the cursor back where they had it. You can figure out where the cursor should be more easily from onTextChanged than from afterTextChanged.

Something like this:

class PhoneNumberMask(val editText: EditText) : TextWatcher {
    private var isRunning: Boolean = false

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

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if (isRunning) {
            return
        }

        var cursorPosition = start + count
        val digits = s.filter(Char::isDigit)
            .dropWhile { it == '0' }
            .take(10)
        cursorPosition -= s.take(cursorPosition).run {
            count { !it.isDigit() } + filter(Char::digit).takeWhile { it == '0' }.count()
        }

        val output = StringBuilder(digits)

        fun punctuate(position: Int, punctuation: String) {
            if (digits.length > position) {
                output.insert(position, punctuation)
                if (cursorPosition > position) {
                    cursorPosition += punctuation.length
                }
            }
        }

        punctuate(8, " ")
        punctuate(6, " ")
        punctuate(3, ") ")
        punctuate(0, "(")

        isRunning = true
        editText.setText(output)
        editText.setSelection(cursorPosition.coerceAtMost(output.length))
        isRunning = false
    }

    override fun afterTextChanged(s: Editable) {
    }
}