You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中EditText手机号格式化TextWatcher的问题咨询

Hey there! Let's fix those two phone number formatting issues you're facing—preventing a leading zero and allowing proper deletion of spaces. Here's how to adjust your PhoneNumberMask class to meet your target format (555) 555 55 55:

First, let's break down the fixes:

  1. Prevent leading zero: We'll block users from entering 0 as the first digit, covering both direct input and pasted text cases.
  2. Allow deleting spaces: The original logic relied on the formatted text length (including spaces/symbols) instead of actual digit count. We'll switch to processing only numeric characters first, then rebuild the formatted string from scratch—this way, deleting spaces won't trigger forced re-addition.

Modified Code

class PhoneNumberMask(val editText: EditText) : TextWatcher { 
    var isRunning: Boolean = false 
    private val maxDigits = 10 // Matches your target format: 3+3+2+2=10 digits
    
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } 
    
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { 
        // Block leading zero when entering the first character
        if (start == 0 && before == 0 && count == 1 && s?.get(0) == '0') {
            editText.setText("")
            editText.setSelection(0)
        }
    } 
    
    override fun afterTextChanged(s: Editable) { 
        if (isRunning) return 
        isRunning = true 
        
        // Extract only numeric characters from input
        val digits = s.toString().replace(Regex("[^\\d]"), "")
        
        // Prevent leading zero in case it slips through (e.g., pasted text)
        if (digits.startsWith("0")) {
            editText.setText("")
            isRunning = false
            return
        }
        
        // Build the formatted phone number string
        val formattedNumber = buildString {
            when (digits.length) {
                0 -> append("")
                in 1..3 -> {
                    append("(")
                    append(digits)
                }
                in 4..6 -> {
                    append("(${digits.substring(0,3)}) ")
                    append(digits.substring(3))
                }
                in 7..8 -> {
                    append("(${digits.substring(0,3)}) ${digits.substring(3,6)} ")
                    append(digits.substring(6))
                }
                in 9..maxDigits -> {
                    append("(${digits.substring(0,3)}) ${digits.substring(3,6)} ${digits.substring(6,8)} ")
                    append(digits.substring(8))
                }
                else -> {
                    // Trim to max digits if input exceeds 10
                    val trimmedDigits = digits.substring(0, maxDigits)
                    append("(${trimmedDigits.substring(0,3)}) ${trimmedDigits.substring(3,6)} ${trimmedDigits.substring(6,8)} ${trimmedDigits.substring(8)}")
                }
            }
        }
        
        // Calculate cursor position relative to digits (fixes cursor jumping)
        val cursorInDigits = calculateCursorPositionInDigits(s, editText.selectionStart)
        editText.setText(formattedNumber)
        val newCursorPos = calculateNewCursorPosition(formattedNumber, cursorInDigits)
        editText.setSelection(newCursorPos.coerceAtMost(formattedNumber.length))
        
        isRunning = false 
    }
    
    /**
     * Convert cursor position in formatted text to position in pure digits
     */
    private fun calculateCursorPositionInDigits(text: CharSequence, cursorPos: Int): Int {
        var digitCount = 0
        for (i in 0 until cursorPos) {
            if (text[i].isDigit()) digitCount++
        }
        return digitCount
    }
    
    /**
     * Convert digit position back to cursor position in formatted text
     */
    private fun calculateNewCursorPosition(formattedText: String, digitPos: Int): Int {
        var currentDigit = 0
        var cursorPos = 0
        for (char in formattedText) {
            if (currentDigit == digitPos) break
            if (char.isDigit()) currentDigit++
            cursorPos++
        }
        return cursorPos
    }
}

Key Improvements:

  • Leading zero prevention: We check both during input (in onTextChanged) and after digit extraction (in afterTextChanged) to cover all edge cases.
  • Space deletion fix: By working with pure digits first, we rebuild the formatted string every time—deleting a space just triggers a reformat based on remaining digits, so spaces only appear where they should.
  • Better cursor handling: The two helper methods ensure the cursor stays in the correct position when editing, instead of jumping to the end every time.

To use this, attach it to your EditText like before:

yourPhoneEditText.addTextChangedListener(PhoneNumberMask(yourPhoneEditText))

内容的提问来源于stack exchange,提问作者Mesut Durmuser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 19:02:51