简体   繁体   中英

Phone number auto formatting in android

I need to format a phone number in an edit text in android as per this format (222) 222-2222 ext222222 I have created a watcher that does the auto formatting for me. The watcher works fine and formats the phone number correctly. My only issue is when somebody manually goes to some other position within the entered phone and starts deleting or adding numbers then the auto formatting does not work. Any ideas how to go about with this issue. Here is how my current watcher looks like:

editText.addTextChangedListener(object : TextWatcher {
    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}

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

    override fun afterTextChanged(s: Editable) {
        val text = editText.text.toString()
        val textLength = editText.text.length
        if (text.endsWith("-") || text.endsWith(" ")) {
            return
        }
        if (textLength == 1) {
            if (!text.contains("(")) {
                editText.setText(StringBuilder(text).insert(text.length - 1, "(").toString())
                editText.setSelection(editText.text.length)
            }
        } else if (textLength == 5) {
            if (!text.contains(")")) {
                editText.setText(StringBuilder(text).insert(text.length - 1, ")").toString())
                editText.setSelection(editText.text.length)
            }
        } else if (textLength == 6) {
            editText.setText(StringBuilder(text).insert(text.length - 1, " ").toString())
            editText.setSelection(editText.text.length)
        } else if (textLength == 10) {
            if (!text.contains("-")) {
                editText.setText(StringBuilder(text).insert(text.length - 1, "-").toString())
                editText.setSelection(editText.text.length)
            }
        } else if (textLength == 15) {
            if (text.contains("-")) {
                editText.setText(StringBuilder(text).insert(text.length - 1, " ext").toString())
                editText.setSelection(editText.text.length)
            }
        }
    }
})

If all could depend upon me, this is how I could handle that(see comments between lines). This is only for the phone number formatting, assume it's a regional number with max 10 digits:

phoneNumber.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {




            }

            @Override
            public void afterTextChanged(Editable s) {

                /* Let me prepare a StringBuilder to hold all digits of the edit text */
                    StringBuilder digits = new StringBuilder();

                 /* this is the phone StringBuilder that will hold the phone number */

                    StringBuilder phone = new StringBuilder();

                 /* let's take all characters from the edit text */
                    char[] chars = phoneNumber.getText().toString().toCharArray();

                   /* a loop to extract all digits */
                    for (int x = 0; x < chars.length; x++) {
                        if (Character.isDigit(chars[x])) {
                            /* if its a digit append to digits string builder */
                            digits.append(chars[x]);
                        }
                    }


                    if (digits.toString().length() >=3) {
                        /* our phone formatting starts at the third character  and starts with the country code*/
                        String countryCode = new String();

                        /* we build the country code */
                        countryCode += "(" + digits.toString().substring(0, 3) + ") ";

                        /** and we append it to phone string builder **/
                        phone.append(countryCode);

                        /** if digits are more than or just 6, that means we already have our state code/region code **/
                        if (digits.toString().length()>=6)
                        {

                            String regionCode=new String();
                            /** we build the state/region code **/
                            regionCode+=digits.toString().substring(3,6)+"-";
                            /** we append the region code to phone **/
                            phone.append(regionCode);



                            /** the phone number will not go over 12 digits  if ten, set the limit to ten digits**/
                            if (digits.toString().length()>=10)
                            {
                                phone.append(digits.toString().substring(6,10));
                            }else
                            {
                                phone.append(digits.toString().substring(6));
                            }
                        }else
                        {
                            phone.append(digits.toString().substring(3));
                        }
                        /** remove the watcher  so you can not capture the affectation you are going to make, to avoid infinite loop on text change **/
                        phoneNumber.removeTextChangedListener(this);

                        /** set the new text to the EditText **/
                        phoneNumber.setText(phone.toString());
                        /** bring the cursor to the end of input **/
                        phoneNumber.setSelection(phoneNumber.getText().toString().length());
                        /* bring back the watcher and go on listening to change events */
                        phoneNumber.addTextChangedListener(this);

                    } else {
                        return;
                    }

            }
        });

This code will reformat the phone number every time the user changes the value of the EditText. I've tested it myself and it works and does not crash. You can test.

EDIT: this is a java code but I think you can easily rewrite it into Kotlin.

You should remove the watcher to avoid triggering afterTextChanged more than once and restore it just after setting the method text.

editText.addTextChangedListener(object : TextWatcher {
    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

    override fun afterTextChanged(editable: Editable) {
        val text = editText.text.toString()
        val textLength = editText.text.length

        if (text.endsWith("-") || text.endsWith(" ")) {
            return
        }

        if (textLength == 1) {
            if (!text.contains("(")) {
                setText(StringBuilder(text).insert(text.length - 1, "(").toString())
            }
        } else if (textLength == 5) {
            if (!text.contains(")")) {
                setText(StringBuilder(text).insert(text.length - 1, ")").toString())
            }
        } else if (textLength == 6) {
            setText(StringBuilder(text).insert(text.length - 1, " ").toString())
        } else if (textLength == 10) {
            if (!text.contains("-")) {
                setText(StringBuilder(text).insert(text.length - 1, "-").toString())
            }
        } else if (textLength == 15) {
            if (text.contains("-")) {
                setText(StringBuilder(text).insert(text.length - 1, " ext").toString())
            }
        }
    }

    private fun setText(text: String){
        editText.removeTextChangedListener(this)
        editText.editableText.replace(0, editText.text.length, text)
        editText.setSelection(text.length)
        editText.addTextChangedListener(this)
    }
})

After these changes, it works fine, but it crashes in the ext part (Not related to your question). Alternatively, you could use libphonenumber AsYouType feature to save some work

This code will resolve issue for back button after adding dash in-between

 editText.addTextChangedListener(object :
                TextWatcher {
           var isBackspaceClicked = false
            override fun beforeTextChanged(p0: CharSequence?, start: Int, count: Int, after: Int) {
                isBackspaceClicked = after < count
            }

            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {            }

            override fun afterTextChanged(s: Editable?) {
            if (!isBackspaceClicked){
                val digits = StringBuilder()
                val phone = StringBuilder()
                val chars: CharArray = editText!!.text.toString().toCharArray()
                for (x in chars.indices) {
                    if (Character.isDigit(chars[x])) {
                        digits.append(chars[x])
                    }
                }

                if (digits.toString().length >= 3) {
                    var countryCode = String()
                    countryCode += "" + digits.toString().substring(0, 3) + "-"
                    phone.append(countryCode)
                    if (digits.toString().length >= 6) {
                        var regionCode = String()
                        regionCode += digits.toString().substring(3, 6) + "-"
                        phone.append(regionCode)
                        if (digits.toString().length >= 10) {
                            phone.append(digits.toString().substring(6, 10))
                        } else {
                            phone.append(digits.toString().substring(6))
                        }
                    } else {
                        phone.append(digits.toString().substring(3))
                    }
                   editText!!.removeTextChangedListener(this)
                    editText!!.setText(phone.toString())
                    editText!!.setSelection(tilPhoneNumber.editText!!.getText().toString().length)
                    editText!!.addTextChangedListener(
                        this
                    )
                } else {
                    return
                }
            }`enter code here`

            }
        })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM