Phone number formatting an EditText in Android

Android

Android Problem Overview


I am making a simple Address Book app (targeting 4.2) that takes name, address, city, state, zip and phone.

I want to format the phone number input as a phone number (XXX) XXX-XXXX, but I need to pull the value out as a string so I can store it in my database when I save. How can i do this?? I have the EditText set for "phone number" input but that obviously doesn't do too much.

Android Solutions


Solution 1 - Android

Simply use the PhoneNumberFormattingTextWatcher, just call:

editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher());

Addition
To be clear, PhoneNumberFormattingTextWatcher's backbone is the PhoneNumberUtils class. The difference is the TextWatcher maintains the EditText while you must call PhoneNumberUtils.formatNumber() every time you change its contents.

Solution 2 - Android

There is a library called PhoneNumberUtils that can help you to cope with phone number conversions and comparisons. For instance, use ...

EditText text = (EditText) findViewById(R.id.editTextId);
PhoneNumberUtils.formatNumber(text.getText().toString())

... to format your number in a standard format.

PhoneNumberUtils.compare(String a, String b);

... helps with fuzzy comparisons. There are lots more. Check out http://developer.android.com/reference/android/telephony/PhoneNumberUtils.html for more.

p.s. setting the the EditText to phone is already a good choice; eventually it might be helpful to add digits e.g. in your layout it looks as ...

<EditText
    android:id="@+id/editTextId"
    android:inputType="phone"
    android:digits="0123456789+" 
/> 

Solution 3 - Android

Simply Use This :

In Java Code :

editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher());

In XML Code :

<EditText
    android:id="@+id/etPhoneNumber"
    android:inputType="phone"/>

This code work for me. It'll auto format when text changed in edit text.

Solution 4 - Android

I've recently done a similar formatting like 1 (XXX) XXX-XXXX for Android EditText. Please find the code below. Just use the TextWatcher sub-class as the text changed listener : ....

UsPhoneNumberFormatter addLineNumberFormatter = new UsPhoneNumberFormatter(
			new WeakReference<EditText>(mYourEditText));
	mYourEditText.addTextChangedListener(addLineNumberFormatter);

...

private class UsPhoneNumberFormatter implements TextWatcher {
	//This TextWatcher sub-class formats entered numbers as 1 (123) 456-7890
	private boolean mFormatting; // this is a flag which prevents the
									// stack(onTextChanged)
	private boolean clearFlag;
	private int mLastStartLocation;
	private String mLastBeforeText;
	private WeakReference<EditText> mWeakEditText;

	public UsPhoneNumberFormatter(WeakReference<EditText> weakEditText) {
		this.mWeakEditText = weakEditText;
	}

	@Override
	public void beforeTextChanged(CharSequence s, int start, int count,
			int after) {
		if (after == 0 && s.toString().equals("1 ")) {
			clearFlag = true;
		}
		mLastStartLocation = start;
		mLastBeforeText = s.toString();
	}

	@Override
	public void onTextChanged(CharSequence s, int start, int before,
			int count) {
		// TODO: Do nothing
	}

	@Override
	public void afterTextChanged(Editable s) {
		// Make sure to ignore calls to afterTextChanged caused by the work
		// done below
		if (!mFormatting) {
			mFormatting = true;
			int curPos = mLastStartLocation;
			String beforeValue = mLastBeforeText;
			String currentValue = s.toString();
			String formattedValue = formatUsNumber(s);
			if (currentValue.length() > beforeValue.length()) {
				int setCusorPos = formattedValue.length()
						- (beforeValue.length() - curPos);
				mWeakEditText.get().setSelection(setCusorPos < 0 ? 0 : setCusorPos);
			} else {
				int setCusorPos = formattedValue.length()
						- (currentValue.length() - curPos);
				if(setCusorPos > 0 && !Character.isDigit(formattedValue.charAt(setCusorPos -1))){
					setCusorPos--;
				}
				mWeakEditText.get().setSelection(setCusorPos < 0 ? 0 : setCusorPos);
			}
			mFormatting = false;
		}
	}

	private String formatUsNumber(Editable text) {
		StringBuilder formattedString = new StringBuilder();
		// Remove everything except digits
		int p = 0;
		while (p < text.length()) {
			char ch = text.charAt(p);
			if (!Character.isDigit(ch)) {
				text.delete(p, p + 1);
			} else {
				p++;
			}
		}
		// Now only digits are remaining
		String allDigitString = text.toString();

		int totalDigitCount = allDigitString.length();

		if (totalDigitCount == 0
				|| (totalDigitCount > 10 && !allDigitString.startsWith("1"))
				|| totalDigitCount > 11) {
			// May be the total length of input length is greater than the
			// expected value so we'll remove all formatting
			text.clear();
			text.append(allDigitString);
			return allDigitString;
		}
		int alreadyPlacedDigitCount = 0;
		// Only '1' is remaining and user pressed backspace and so we clear
		// the edit text.
		if (allDigitString.equals("1") && clearFlag) {
			text.clear();
			clearFlag = false;
			return "";
		}
		if (allDigitString.startsWith("1")) {
			formattedString.append("1 ");
			alreadyPlacedDigitCount++;
		}
		// The first 3 numbers beyond '1' must be enclosed in brackets "()"
		if (totalDigitCount - alreadyPlacedDigitCount > 3) {
			formattedString.append("("
					+ allDigitString.substring(alreadyPlacedDigitCount,
							alreadyPlacedDigitCount + 3) + ") ");
			alreadyPlacedDigitCount += 3;
		}
		// There must be a '-' inserted after the next 3 numbers
		if (totalDigitCount - alreadyPlacedDigitCount > 3) {
			formattedString.append(allDigitString.substring(
					alreadyPlacedDigitCount, alreadyPlacedDigitCount + 3)
					+ "-");
			alreadyPlacedDigitCount += 3;
		}
		// All the required formatting is done so we'll just copy the
		// remaining digits.
		if (totalDigitCount > alreadyPlacedDigitCount) {
			formattedString.append(allDigitString
					.substring(alreadyPlacedDigitCount));
		}

		text.clear();
		text.append(formattedString.toString());
		return formattedString.toString();
	}

}

Solution 5 - Android

Maybe below sample project helps you;

https://github.com/reinaldoarrosi/MaskedEditText

That project contains a view class call MaskedEditText. As first, you should add it in your project.

Then you add below xml part in res/values/attrs.xml file of project;

<resources>
	<declare-styleable name="MaskedEditText">
	    <attr name="mask" format="string" />
	    <attr name="placeholder" format="string" />
	</declare-styleable>
</resources>

Then you will be ready to use MaskedEditText view.

As last, you should add MaskedEditText in your xml file what you want like below;

<packagename.currentfolder.MaskedEditText
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/maskedEditText"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ems="10"
    android:text="5"
    app:mask="(999) 999-9999"
    app:placeholder="_" >

Of course that, you can use it programmatically.

After those steps, adding MaskedEditText will appear like below;

enter image description here

As programmatically, if you want to take it's text value as unmasked, you may use below row;

maskedEditText.getText(true);

To take masked value, you may send false value instead of true value in the getText method.

Solution 6 - Android

You need to create a class:

public class PhoneTextFormatter implements TextWatcher {

    private final String TAG = this.getClass().getSimpleName();

    private EditText mEditText;

    private String mPattern;

    public PhoneTextFormatter(EditText editText, String pattern) {
        mEditText = editText;
        mPattern = pattern;
        //set max length of string
        int maxLength = pattern.length();
        mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        StringBuilder phone = new StringBuilder(s);

        Log.d(TAG, "join");

        if (count > 0 && !isValid(phone.toString())) {
            for (int i = 0; i < phone.length(); i++) {
                Log.d(TAG, String.format("%s", phone));
                char c = mPattern.charAt(i);

                if ((c != '#') && (c != phone.charAt(i))) {
                    phone.insert(i, c);
                }
            }

            mEditText.setText(phone);
            mEditText.setSelection(mEditText.getText().length());
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }

    private boolean isValid(String phone)
    {
        for (int i = 0; i < phone.length(); i++) {
            char c = mPattern.charAt(i);

            if (c == '#') continue;

            if (c != phone.charAt(i)) {
                return false;
            }
        }

        return true;
    }
}

Use this as follows:

phone = view.findViewById(R.id.phone);
phone.addTextChangedListener(new PhoneTextFormatter(phone, "+7 (###) ###-####"));

Solution 7 - Android

Follow the instructions in this Answer to format the EditText mask.

> https://stackoverflow.com/a/34907607/1013929

And after that, you can catch the original numbers from the masked string with:

String phoneNumbers = maskedString.replaceAll("[^\\d]", "");

Solution 8 - Android

//(123) 456 7890  formate set

private int textlength = 0;

public class MyPhoneTextWatcher implements TextWatcher {

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    }
    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {


        String text = etMobile.getText().toString();
        textlength = etMobile.getText().length();

        if (text.endsWith(" "))
            return;

        if (textlength == 1) {
            if (!text.contains("(")) {
                etMobile.setText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
                etMobile.setSelection(etMobile.getText().length());
            }

        } else if (textlength == 5) {

            if (!text.contains(")")) {
                etMobile.setText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
                etMobile.setSelection(etMobile.getText().length());
            }

        } else if (textlength == 6 || textlength == 10) {
            etMobile.setText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
            etMobile.setSelection(etMobile.getText().length());
        }

    }
    @Override
    public void afterTextChanged(Editable editable) {
    }
}

Solution 9 - Android

More like clean:

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

String text = etyEditText.getText();
	int textlength = etyEditText.getText().length();

	if (text.endsWith("(") ||text.endsWith(")")|| text.endsWith(" ") || text.endsWith("-")  )
                return;

	switch (textlength){
		case 1:
			etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
			etyEditText.setSelection(etyEditText.getText().length());
			break;
		case 5:
			etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
			etyEditText.setSelection(etyEditText.getText().length());
			break;
		case 6:
			etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
			etyEditText.setSelection(etyEditText.getText().length());
			break;
		case 10:
			etyEditText.setEditText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
			etyEditText.setSelection(etyEditText.getText().length());
			break;
	}

}

Solution 10 - Android

You can use spawns to format phone numbers in Android. This solution is better than the others because it does not change input text. Formatting remains purely visual.

implementation 'com.googlecode.libphonenumber:libphonenumber:7.0.4'

Formatter class:

open class PhoneNumberFormatter : TransformationMethod {
private val mFormatter: AsYouTypeFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(Locale.getDefault().country)

override fun getTransformation(source: CharSequence, view: View): CharSequence {
    val formatted = format(source)
    if (source is Spannable) {
        setSpans(source, formatted)
        return source
    }
    return formatted
}
override fun onFocusChanged(view: View?, sourceText: CharSequence?, focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) = Unit

private fun setSpans(spannable: Spannable, formatted: CharSequence): CharSequence {

    spannable.clearSpawns()

    var charterIndex = 0
    var formattedIndex = 0
    var spawn = ""
    val spawns: List<String> = spannable
        .map {
            spawn = ""
            charterIndex = formatted.indexOf(it, formattedIndex)
            if (charterIndex != -1){
                spawn = formatted.substring(formattedIndex, charterIndex-1)
                formattedIndex = charterIndex+1
            }
            spawn
        }

    spawns.forEachIndexed { index, sequence ->
        spannable.setSpan(CharterSpan(sequence), index, index + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
    }

    return formatted
}

private fun Spannable.clearSpawns() =
    this
        .getSpans(0, this.length, CharterSpan::class.java)
        .forEach { this.removeSpan(it) }

private fun format(spannable: CharSequence): String {
    mFormatter.clear()
    var formated = ""
    for (i in 0 until spannable.length) {
        formated = mFormatter.inputDigit(spannable[i])
    }
    return formated
}

private inner class CharterSpan(private val charters: String) : ReplacementSpan() {

    var space = 0

    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
        space = Math.round(paint.measureText(charters, 0, charters.length))
        return Math.round(paint.measureText(text, start, end)) + space
    }

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        space = Math.round(paint.measureText(charters, 0, charters.length))
        canvas.drawText(text, start, end, x + space, y.toFloat(), paint)
        canvas.drawText(charters, x, y.toFloat(), paint)
    }
    }

}

Uasge:

editText.transformationMethod = formatter

Solution 11 - Android

You can use a Regular Expression with pattern matching to extract number from a string.

    String s="";
    Pattern p = Pattern.compile("\\d+");
    Matcher m = p.matcher("(1111)123-456-789"); //editText.getText().toString()                                      
    while (m.find()) {
    s=s+m.group(0);
    }
    System.out.println("............"+s);    

    Output : ............1111123456789

Solution 12 - Android

If you're only interested in international numbers and you'd like to be able to show the flag of the country that matches the country code in the input, I wrote a small library for that:

https://github.com/tfcporciuncula/phonemoji

Here's how it looks:

library demo

Solution 13 - Android

Don't worry. I have make a most of better solution for you. You can see this simple app link below.

private EditText mPasswordField;
public int textLength = 0;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    mPasswordField = (EditText) findViewById(R.id.password_field);
    mPasswordField.addTextChangedListener(this);
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

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


    String text = mPasswordField.getText().toString();
    textLength = mPasswordField.getText().length();

    if (text.endsWith("-") || text.endsWith(" ") || text.endsWith(" "))
        return;

    if (textLength == 1) {
        if (!text.contains("(")) {
            mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
            mPasswordField.setSelection(mPasswordField.getText().length());
        }

    } else if (textLength == 5) {

        if (!text.contains(")")) {
            mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
            mPasswordField.setSelection(mPasswordField.getText().length());
        }

    } else if (textLength == 6) {
            mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
            mPasswordField.setSelection(mPasswordField.getText().length());

    } else if (textLength == 10) {
        if (!text.contains("-")) {
            mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
            mPasswordField.setSelection(mPasswordField.getText().length());
        }
    } else if (textLength == 15) {
        if (text.contains("-")) {
            mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
            mPasswordField.setSelection(mPasswordField.getText().length());
        }
    }else if (textLength == 18) {
        if (text.contains("-")) {
            mPasswordField.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
            mPasswordField.setSelection(mPasswordField.getText().length());
        }
    } else if (textLength == 20) {
        Intent i = new Intent(MainActivity.this, Activity2.class);
        startActivity(i);

    }



}

@Override
public void afterTextChanged(Editable s) {

}

Not: Don't forget "implement TextWatcher" with your activity class.

Link :https://drive.google.com/open?id=0B-yo9VvU7jyBMjJpT29xc2k5bnc

Hope you are feeling cool for this solution.

Solution 14 - Android

You can accept only numbers and phone number type using java code

 EditText number1 = (EditText) layout.findViewById(R.id.edittext); 
    number1.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_CLASS_PHONE);
     number1.setKeyListener(DigitsKeyListener.getInstance("0123456789”));
      number1.setFilters(new InputFilter[] {new InputFilter.LengthFilter(14)}); // 14 is max digits

This code will avoid lot of validations after reading input

Solution 15 - Android

This code is work for me for (216) 555-5555

etphonenumber.addTextChangedListener(new TextWatcher()
        {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count)
            {
                // TODO Auto-generated method stub
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after)
            {
                // TODO Auto-generated method stub
            }
            @Override
            public void afterTextChanged(Editable s)
            {
                String text = etphonenumber.getText().toString();
                int  textLength = etphonenumber.getText().length();
                if (text.endsWith("-") || text.endsWith(" ") || text.endsWith(" "))
                    return;
                if (textLength == 1) {
                    if (!text.contains("("))
                    {
                        etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, "(").toString());
                        etphonenumber.setSelection(etphonenumber.getText().length());
                    }
                }
                else if (textLength == 5)
                {
                    if (!text.contains(")"))
                    {
                        etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, ")").toString());
                        etphonenumber.setSelection(etphonenumber.getText().length());
                    }
                }
                else if (textLength == 6)
                {
                    etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, " ").toString());
                    etphonenumber.setSelection(etphonenumber.getText().length());
                }
                else if (textLength == 10)
                {
                    if (!text.contains("-"))
                    {
                        etphonenumber.setText(new StringBuilder(text).insert(text.length() - 1, "-").toString());
                        etphonenumber.setSelection(etphonenumber.getText().length());
                    }
                }
            }
        });

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBackDoorNoBabyView Question on Stackoverflow
Solution 1 - AndroidSamView Answer on Stackoverflow
Solution 2 - AndroidTrinimonView Answer on Stackoverflow
Solution 3 - AndroidSANATView Answer on Stackoverflow
Solution 4 - AndroidSamik BandyopadhyayView Answer on Stackoverflow
Solution 5 - AndroidoguzhanView Answer on Stackoverflow
Solution 6 - AndroidYour sad storyView Answer on Stackoverflow
Solution 7 - AndroidMaurício FonsecaView Answer on Stackoverflow
Solution 8 - AndroidKailash BarochiyaView Answer on Stackoverflow
Solution 9 - AndroidSerkan SekmanView Answer on Stackoverflow
Solution 10 - AndroidDmitry SubbotenkoView Answer on Stackoverflow
Solution 11 - AndroidRaghunandanView Answer on Stackoverflow
Solution 12 - AndroidFred PorciúnculaView Answer on Stackoverflow
Solution 13 - AndroidAl ImranView Answer on Stackoverflow
Solution 14 - AndroidAkhila MadariView Answer on Stackoverflow
Solution 15 - AndroidRandika WanninayakaView Answer on Stackoverflow