Android 使用电话号码格式屏蔽EditText,如PhoneNumberRutils中的格式

Android 使用电话号码格式屏蔽EditText,如PhoneNumberRutils中的格式,android,formatting,android-edittext,masking,Android,Formatting,Android Edittext,Masking,我想让用户在editText中输入电话号码,以便在用户每次输入号码时动态更改格式。也就是说,当用户输入多达4位数字时,如7144,编辑文本显示“714-4”。 我希望editText在用户输入数字时动态更新为############格式。如何做到这一点?另外,我处理的编辑文本不止一个。最简单的方法是使用内置的Android 所以,基本上你可以在代码中获得你的编辑文本,然后像这样设置你的文本监视程序 EditText inputField = (EditText) findViewById(R.i

我想让用户在editText中输入电话号码,以便在用户每次输入号码时动态更改格式。也就是说,当用户输入多达4位数字时,如7144,编辑文本显示“714-4”。
我希望editText在用户输入数字时动态更新为############格式。如何做到这一点?另外,我处理的编辑文本不止一个。

最简单的方法是使用内置的Android

所以,基本上你可以在代码中获得你的编辑文本,然后像这样设置你的文本监视程序

EditText inputField = (EditText) findViewById(R.id.inputfield);
inputField.addTextChangedListener(new PhoneNumberFormattingTextWatcher());

使用PhoneNumberFormattingTextWatcher的好处在于,它可以根据您所在的地区正确设置您的号码输入格式。

以上答案是正确的,但它适用于特定国家/地区。如果有人想要这样的格式化电话号码(###-#-#-#-#)。然后用这个:

etPhoneNumber.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    int digits = etPhoneNumber.getText().toString().length();
                    if (digits > 1)
                        lastChar = etPhoneNumber.getText().toString().substring(digits-1);
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    int digits = etPhoneNumber.getText().toString().length();
                    Log.d("LENGTH",""+digits);
                    if (!lastChar.equals("-")) {
                        if (digits == 3 || digits == 7) {
                            etPhoneNumber.append("-");
                        }
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {

                }
            });
在活动中声明
String lastChar=”“

现在在edittext的xml中添加这一行

android:inputType="phone"
就这些

已编辑:如果您希望编辑文本长度限制为10位,请在下面添加行:

android:maxLength="12"

(它是12,因为“-”将占用两倍的空间)

只需将以下内容添加到电话号码的编辑文本中,即可获得格式化的电话号码(###-##-###)

Phone.addTextChangedListener(新的TextWatcher(){
int-length_=0;
@凌驾
更改前文本之前的公共void(字符序列s、int start、int count、int after){
长度=s.长度();
}
@凌驾
public void onTextChanged(字符序列、int start、int before、int count){
}
@凌驾
公共无效后文本已更改(可编辑){
if(长度在3){
if(字符isDigit(s.charAt(3)))
s、 插入(3),“-”;
}
如果(s.长度()>7){
if(字符isDigit(s.charAt(7)))
s、 插入(7),“-”;
}
}
}
});

我的脚本,示例取自此处



上述解决方案不考虑退格,因此当您在键入后删除一些数字时,格式往往会混乱。下面的代码更正了此问题

phoneNumberEditText.addTextChangedListener(new TextWatcher() {

        int beforeLength;

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            beforeLength = phoneNumberEditText.length();
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            int digits = phoneNumberEditText.getText().toString().length();
            if (beforeLength < digits && (digits == 3 || digits == 7)) {
                phoneNumberEditText.append("-");
            }
        }

        @Override
        public void afterTextChanged(Editable s) { }
    });
phonenumberditext.addTextChangedListener(新的TextWatcher(){
长度前的整数;
@凌驾
更改前文本之前的公共void(字符序列s、int start、int count、int after){
beforeLength=phoneNumberEditText.length();
}
@凌驾
public void onTextChanged(字符序列、int start、int before、int count){
int digits=phonenumberditext.getText().toString().length();
如果(在长度<位数&(位数==3 | |位数==7)之前){
PhoneNumberItemText.append(“-”);
}
}
@凌驾
公共无效后文本更改(可编辑的s){}
});

此代码允许您输入带掩码的电话号码##############################

editText.addTextChangedListener(new TextWatcher() {
            final static String DELIMITER = "-";
            String lastChar;

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                int digits = editText.getText().toString().length();
                if (digits > 1)
                    lastChar = editText.getText().toString().substring(digits-1);
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                int digits = editText.getText().length();
                // prevent input dash by user
                if (digits > 0 && digits != 4 && digits != 8) {
                    CharSequence last = s.subSequence(digits - 1, digits);
                    if (last.toString().equals(DELIMITER))
                        editText.getText().delete(digits - 1, digits);
                }
                // inset and remove dash
                if (digits == 3 || digits == 7) {
                    if (!lastChar.equals(DELIMITER))
                        editText.append("-"); // insert a dash
                    else
                        editText.getText().delete(digits -1, digits); // delete last digit with a dash
                }
                dataModel.setPhone(s.toString());
            }

            @Override
            public void afterTextChanged(Editable s) {}
        });
布局:

<EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:imeOptions="actionDone"
            android:textAlignment="textStart"
            android:inputType="number"
            android:digits="-0123456789"
            android:lines="1"
            android:maxLength="12"/>
    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:digits=" 0123456789"
        android:inputType="numberPassword"
        android:maxLength="14"
        tools:text="Example text" />

这是我的解决方案

如何在活动/片段中运行(在onViewCreated中的f.e):

Validatior类:

    import android.text.Editable
    import android.text.TextWatcher
    import android.widget.EditText

    class ExampleIdWatcher(exampleIdInput: EditText) : TextWatcher {

        private var exampleIdInput: EditText = exampleIdInput

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

        override fun onTextChanged(userInput: CharSequence?, start: Int, before: Int, count: Int) {
            if (userInput!!.isNotEmpty() && !areSpacesCorrect(userInput)) {

                val stringTextWithoutWhiteSpaces: String = userInput.toString().replace(" ", "")

                val textSB: StringBuilder = StringBuilder(stringTextWithoutWhiteSpaces)

                when {
                    textSB.length > 8 -> {
                        setSpacesAndCursorPosition(textSB, 2, 6, 10)
                    }
                    textSB.length > 5 -> {
                        setSpacesAndCursorPosition(textSB, 2, 6)
                    }
                    textSB.length > 2 -> {
                        setSpacesAndCursorPosition(textSB, 2)
                    }
                }
            }
        }

        override fun afterTextChanged(s: Editable?) {
        }

        private fun setSpacesAndCursorPosition(textSB: StringBuilder, vararg ts: Int) {
            for (t in ts) // ts is an Array
                textSB.insert(t, SPACE_CHAR)
            val currentCursorPosition = getCursorPosition(exampleIdInput.selectionStart)
            exampleIdInput.setText(textSB.toString())
            exampleIdInput.setSelection(currentCursorPosition)
        }

        private fun getCursorPosition(currentCursorPosition: Int): Int {
            return if (EXAMPLE_ID_SPACE_CHAR_CURSOR_POSITIONS.contains(currentCursorPosition)) {
                currentCursorPosition + 1
            } else {
                currentCursorPosition
            }
        }

        private fun areSpacesCorrect(userInput: CharSequence?): Boolean {
            EXAMPLE_ID_SPACE_CHAR_INDEXES.forEach {
                if (userInput!!.length > it && userInput[it].toString() != SPACE_CHAR) {
                    return false
                }
            }
            return true
        }

        companion object {
            private val EXAMPLE_ID_SPACE_CHAR_INDEXES: List<Int> = listOf(2, 6, 10)
            private val EXAMPLE_ID_SPACE_CHAR_CURSOR_POSITIONS: List<Int> = EXAMPLE_ID_SPACE_CHAR_INDEXES.map { it + 1 }
            private const val SPACE_CHAR: String = " "
        }
    }

Kotlin中Android的动态掩码。这是一个工作良好,严格符合电话号码面罩。你可以提供任何口罩

EDIT1:我有一个新版本,可以锁定用户在键盘上键入的不需要的字符

/**
 * Text watcher allowing strictly a MASK with '#' (example: (###) ###-####
 */
class NumberTextWatcher(private var mask: String) : TextWatcher {
    companion object {
        const val MASK_CHAR = '#'
    }

    // simple mutex
    private var isCursorRunning = false
    private var isDeleting = false

    override fun afterTextChanged(s: Editable?) {
        if (isCursorRunning || isDeleting) {
            return
        }
        isCursorRunning = true

        s?.let {
            val onlyDigits = removeMask(it.toString())
            it.clear()
            it.append(applyMask(mask, onlyDigits))
        }

        isCursorRunning = false
    }

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

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

    private fun applyMask(mask: String, onlyDigits: String): String {
        val maskPlaceholderCharCount = mask.count { it == MASK_CHAR }
        var maskCurrentCharIndex = 0
        var output = ""

        onlyDigits.take(min(maskPlaceholderCharCount, onlyDigits.length)).forEach { c ->
            for (i in maskCurrentCharIndex until mask.length) {
                if (mask[i] == MASK_CHAR) {
                    output += c
                    maskCurrentCharIndex += 1
                    break
                } else {
                    output += mask[i]
                    maskCurrentCharIndex = i + 1
                }
            }
        }
        return output
    }

    private fun removeMask(value: String): String {
        // extract all the digits from the string
        return Regex("\\D+").replace(value, "")
    }
}
编辑2:单元测试

class NumberTextWatcherTest {

    @Test
    fun phone_number_test() {
        val phoneNumberMask = "(###) ###-####"
        val phoneNumberTextWatcher = NumberTextWatcher(phoneNumberMask)

        val input = StringBuilder()
        val expectedResult = "(012) 345-6789"
        var result = ""

        // mimic typing 10 digits
        for (i in 0 until 10) {
            input.append(i)
            result = mimicTextInput(phoneNumberTextWatcher, result, i.toString()) ?: ""
        }

        Assert.assertEquals(input.toString(), "0123456789")
        Assert.assertEquals(result, expectedResult)
    }

    @Test
    fun credit_card_test() {
        val creditCardNumberMask = "#### #### #### ####"
        val creditCardNumberTextWatcher = NumberTextWatcher(creditCardNumberMask)

        val input = StringBuilder()
        val expectedResult = "0123 4567 8901 2345"
        var result = ""

        // mimic typing 16 digits
        for (i in 0 until 16) {
            val value = i % 10
            input.append(value)
            result = mimicTextInput(creditCardNumberTextWatcher, result, value.toString()) ?: ""
        }

        Assert.assertEquals(input.toString(), "0123456789012345")
        Assert.assertEquals(result, expectedResult)
    }

    @Test
    fun date_test() {
        val dateMask = "####/##/##"
        val dateTextWatcher = NumberTextWatcher(dateMask)

        val input = "20200504"
        val expectedResult = "2020/05/04"
        val initialInputValue = ""

        val result = mimicTextInput(dateTextWatcher, initialInputValue, input)

        Assert.assertEquals(result, expectedResult)
    }

    @Test
    fun credit_card_expiration_date_test() {
        val creditCardExpirationDateMask = "##/##"
        val creditCardExpirationDateTextWatcher = NumberTextWatcher(creditCardExpirationDateMask)

        val input = "1121"
        val expectedResult = "11/21"
        val initialInputValue = ""

        val result = mimicTextInput(creditCardExpirationDateTextWatcher, initialInputValue, input)

        Assert.assertEquals(result, expectedResult)
    }

    private fun mimicTextInput(textWatcher: TextWatcher, initialInputValue: String, input: String): String? {
        textWatcher.beforeTextChanged(initialInputValue, initialInputValue.length, initialInputValue.length, input.length + initialInputValue.length)
        val newText = initialInputValue + input

        textWatcher.onTextChanged(newText, 1, newText.length - 1, 1)
        val editable: Editable = SpannableStringBuilder(newText)

        textWatcher.afterTextChanged(editable)
        return editable.toString()
    }
}

如果您需要自定义掩码,您会发现此答案很有用:editTextPhone.addTextChangedListener(PhoneNumberFormattingTextWatcher())Kotlin此选项适用于XXX-XXX-XXXX格式化电话号码。只是复制粘贴。这是唯一一个像charm一样工作的最佳解决方案,而这个代码片段可能会解决这个问题,真正有助于提高您的文章质量。请记住,您将在将来回答读者的问题,这些人可能不知道您的代码建议的原因。还请尽量不要用解释性注释挤满你的代码,这会降低代码和解释的可读性!是的,这起到了作用,也处理了背压场景
    import android.text.Editable
    import android.text.TextWatcher
    import android.widget.EditText

    class ExampleIdWatcher(exampleIdInput: EditText) : TextWatcher {

        private var exampleIdInput: EditText = exampleIdInput

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

        override fun onTextChanged(userInput: CharSequence?, start: Int, before: Int, count: Int) {
            if (userInput!!.isNotEmpty() && !areSpacesCorrect(userInput)) {

                val stringTextWithoutWhiteSpaces: String = userInput.toString().replace(" ", "")

                val textSB: StringBuilder = StringBuilder(stringTextWithoutWhiteSpaces)

                when {
                    textSB.length > 8 -> {
                        setSpacesAndCursorPosition(textSB, 2, 6, 10)
                    }
                    textSB.length > 5 -> {
                        setSpacesAndCursorPosition(textSB, 2, 6)
                    }
                    textSB.length > 2 -> {
                        setSpacesAndCursorPosition(textSB, 2)
                    }
                }
            }
        }

        override fun afterTextChanged(s: Editable?) {
        }

        private fun setSpacesAndCursorPosition(textSB: StringBuilder, vararg ts: Int) {
            for (t in ts) // ts is an Array
                textSB.insert(t, SPACE_CHAR)
            val currentCursorPosition = getCursorPosition(exampleIdInput.selectionStart)
            exampleIdInput.setText(textSB.toString())
            exampleIdInput.setSelection(currentCursorPosition)
        }

        private fun getCursorPosition(currentCursorPosition: Int): Int {
            return if (EXAMPLE_ID_SPACE_CHAR_CURSOR_POSITIONS.contains(currentCursorPosition)) {
                currentCursorPosition + 1
            } else {
                currentCursorPosition
            }
        }

        private fun areSpacesCorrect(userInput: CharSequence?): Boolean {
            EXAMPLE_ID_SPACE_CHAR_INDEXES.forEach {
                if (userInput!!.length > it && userInput[it].toString() != SPACE_CHAR) {
                    return false
                }
            }
            return true
        }

        companion object {
            private val EXAMPLE_ID_SPACE_CHAR_INDEXES: List<Int> = listOf(2, 6, 10)
            private val EXAMPLE_ID_SPACE_CHAR_CURSOR_POSITIONS: List<Int> = EXAMPLE_ID_SPACE_CHAR_INDEXES.map { it + 1 }
            private const val SPACE_CHAR: String = " "
        }
    }
    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:digits=" 0123456789"
        android:inputType="numberPassword"
        android:maxLength="14"
        tools:text="Example text" />
XX XXX XXX XXX
/**
 * Text watcher allowing strictly a MASK with '#' (example: (###) ###-####
 */
class NumberTextWatcher(private var mask: String) : TextWatcher {
    companion object {
        const val MASK_CHAR = '#'
    }

    // simple mutex
    private var isCursorRunning = false
    private var isDeleting = false

    override fun afterTextChanged(s: Editable?) {
        if (isCursorRunning || isDeleting) {
            return
        }
        isCursorRunning = true

        s?.let {
            val onlyDigits = removeMask(it.toString())
            it.clear()
            it.append(applyMask(mask, onlyDigits))
        }

        isCursorRunning = false
    }

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

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

    private fun applyMask(mask: String, onlyDigits: String): String {
        val maskPlaceholderCharCount = mask.count { it == MASK_CHAR }
        var maskCurrentCharIndex = 0
        var output = ""

        onlyDigits.take(min(maskPlaceholderCharCount, onlyDigits.length)).forEach { c ->
            for (i in maskCurrentCharIndex until mask.length) {
                if (mask[i] == MASK_CHAR) {
                    output += c
                    maskCurrentCharIndex += 1
                    break
                } else {
                    output += mask[i]
                    maskCurrentCharIndex = i + 1
                }
            }
        }
        return output
    }

    private fun removeMask(value: String): String {
        // extract all the digits from the string
        return Regex("\\D+").replace(value, "")
    }
}
class NumberTextWatcherTest {

    @Test
    fun phone_number_test() {
        val phoneNumberMask = "(###) ###-####"
        val phoneNumberTextWatcher = NumberTextWatcher(phoneNumberMask)

        val input = StringBuilder()
        val expectedResult = "(012) 345-6789"
        var result = ""

        // mimic typing 10 digits
        for (i in 0 until 10) {
            input.append(i)
            result = mimicTextInput(phoneNumberTextWatcher, result, i.toString()) ?: ""
        }

        Assert.assertEquals(input.toString(), "0123456789")
        Assert.assertEquals(result, expectedResult)
    }

    @Test
    fun credit_card_test() {
        val creditCardNumberMask = "#### #### #### ####"
        val creditCardNumberTextWatcher = NumberTextWatcher(creditCardNumberMask)

        val input = StringBuilder()
        val expectedResult = "0123 4567 8901 2345"
        var result = ""

        // mimic typing 16 digits
        for (i in 0 until 16) {
            val value = i % 10
            input.append(value)
            result = mimicTextInput(creditCardNumberTextWatcher, result, value.toString()) ?: ""
        }

        Assert.assertEquals(input.toString(), "0123456789012345")
        Assert.assertEquals(result, expectedResult)
    }

    @Test
    fun date_test() {
        val dateMask = "####/##/##"
        val dateTextWatcher = NumberTextWatcher(dateMask)

        val input = "20200504"
        val expectedResult = "2020/05/04"
        val initialInputValue = ""

        val result = mimicTextInput(dateTextWatcher, initialInputValue, input)

        Assert.assertEquals(result, expectedResult)
    }

    @Test
    fun credit_card_expiration_date_test() {
        val creditCardExpirationDateMask = "##/##"
        val creditCardExpirationDateTextWatcher = NumberTextWatcher(creditCardExpirationDateMask)

        val input = "1121"
        val expectedResult = "11/21"
        val initialInputValue = ""

        val result = mimicTextInput(creditCardExpirationDateTextWatcher, initialInputValue, input)

        Assert.assertEquals(result, expectedResult)
    }

    private fun mimicTextInput(textWatcher: TextWatcher, initialInputValue: String, input: String): String? {
        textWatcher.beforeTextChanged(initialInputValue, initialInputValue.length, initialInputValue.length, input.length + initialInputValue.length)
        val newText = initialInputValue + input

        textWatcher.onTextChanged(newText, 1, newText.length - 1, 1)
        val editable: Editable = SpannableStringBuilder(newText)

        textWatcher.afterTextChanged(editable)
        return editable.toString()
    }
}