I got this block

edTelData.addTextChangedListener(new PhoneNumberFormattingTextWatcher() { //we need to know if the user is erasing or inputing some new character private boolean backspacingFlag = false; //we need to block the :afterTextChanges method to be called again after we just replaced the EditText text private boolean editedFlag = false; //we need to mark the cursor position and restore it after the edition private int cursorComplement; @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //we store the cursor local relative to the end of the string in the EditText before the edition cursorComplement = s.length()-edTelData.getSelectionStart(); //we check if the user ir inputing or erasing a character if (count > after) { backspacingFlag = true; } else { backspacingFlag = false; } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // nothing to do here =D } @Override public void afterTextChanged(Editable s) { String string = s.toString(); //what matters are the phone digits beneath the mask, so we always work with a raw string with only digits String phone = string.replaceAll("[^\\d]", ""); //if the text was just edited, :afterTextChanged is called another time... so we need to verify the flag of edition //if the flag is false, this is a original user-typed entry. so we go on and do some magic if (!editedFlag) { //we start verifying the worst case, many characters mask need to be added //example: 999999999 <- 6+ digits already typed // masked: (999) 999-999 if (phone.length() >= 7 && !backspacingFlag) { //we will edit. next call on this textWatcher will be ignored editedFlag = true; //here is the core. we substring the raw digits and add the mask as convenient String ans = "+7(" + phone.substring(1, 4) + ") " + phone.substring(4,7) + "-" + phone.substring(7); edTelData.setText(ans); //we deliver the cursor to its original position relative to the end of the string edTelData.setSelection(edTelData.getText().length()-cursorComplement); //we end at the most simple case, when just one character mask is needed //example: 99999 <- 3+ digits already typed // masked: (999) 99 } else if (phone.length() >= 4 && !backspacingFlag) { editedFlag = true; String ans = "+7(" +phone.substring(1, 4) + ") " + phone.substring(4); edTelData.setText(ans); edTelData.setSelection(edTelData.getText().length()-cursorComplement); } // We just edited the field, ignoring this cicle of the watcher and getting ready for the next } else { editedFlag = false; } } }); 

now converts a string when typing

+7 (XXX) XXX-XXXX Please help remake in +7 (XXX) XXX-XX-XX (add the second "-")

UPDATED

Thanks for the answers, now the corrected code is ready from below (you can copy-paste)

 edTelData.addTextChangedListener(new PhoneNumberFormattingTextWatcher() { //we need to know if the user is erasing or inputing some new character private boolean backspacingFlag = false; //we need to block the :afterTextChanges method to be called again after we just replaced the EditText text private boolean editedFlag = false; //we need to mark the cursor position and restore it after the edition private int cursorComplement; @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //we store the cursor local relative to the end of the string in the EditText before the edition cursorComplement = s.length()-edTelData.getSelectionStart(); //we check if the user ir inputing or erasing a character if (count > after) { backspacingFlag = true; } else { backspacingFlag = false; } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // nothing to do here =D } @Override public void afterTextChanged(Editable s) { String string = s.toString(); //what matters are the phone digits beneath the mask, so we always work with a raw string with only digits String phone = string.replaceAll("[^\\d]", ""); //if the text was just edited, :afterTextChanged is called another time... so we need to verify the flag of edition //if the flag is false, this is a original user-typed entry. so we go on and do some magic if (!editedFlag) { //we start verifying the worst case, many characters mask need to be added //example: 999999999 <- 6+ digits already typed // masked: (999) 999-999 if (phone.length() >= 9 && !backspacingFlag) { //we will edit. next call on this textWatcher will be ignored editedFlag = true; //here is the core. we substring the raw digits and add the mask as convenient String ans = "+7(" + phone.substring(1, 4) + ") " + phone.substring(4,7) + "-" + phone.substring(7,9) + "-" + phone.substring(9); edTelData.setText(ans); //we deliver the cursor to its original position relative to the end of the string edTelData.setSelection(edTelData.getText().length()-cursorComplement); //we end at the most simple case, when just one character mask is needed //example: 99999 <- 3+ digits already typed // masked: (999) 99 } else if (phone.length() >= 4 && !backspacingFlag) { editedFlag = true; String ans = "+7(" +phone.substring(1, 4) + ") " + phone.substring(4); edTelData.setText(ans); edTelData.setSelection(edTelData.getText().length()-cursorComplement); } // We just edited the field, ignoring this cicle of the watcher and getting ready for the next } else { editedFlag = false; } } }); 

And this is the XML code of the element:

 <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/edTelNumber" android:hint="Номер телефона" android:gravity="center" android:inputType="phone" android:lines="1" android:maxLength="17" android:text="+7" /> 
  • you may have problems if the user decides to enter a number not from 8 but from 9, for example, 9010102032, then you have the first digit “eaten”, you can slightly change the code for this case: else if (phone.length() >= 4 && !backspacingFlag && !string.contains("+7")) { editedFlag = true; String ans = "+7(" + phone.substring(0, 3) + ") " + phone.substring(3); mEditText.setText(ans); mEditText.setSelection(mEditText.getText().length() - cursorComplement); } else if (phone.length() >= 4 && !backspacingFlag && !string.contains("+7")) { editedFlag = true; String ans = "+7(" + phone.substring(0, 3) + ") " + phone.substring(3); mEditText.setText(ans); mEditText.setSelection(mEditText.getText().length() - cursorComplement); } else if (phone.length() >= 4 && !backspacingFlag && !string.contains("+7")) { editedFlag = true; String ans = "+7(" + phone.substring(0, 3) + ") " + phone.substring(3); mEditText.setText(ans); mEditText.setSelection(mEditText.getText().length() - cursorComplement); } - Sergey
  • @ Sergey, I don’t know. If the +7 field already exists, nothing is eaten, if the field is empty, usually in Russia they write after 8 (the country code is always written), and since this block is used for Russian applications, then it is thoughtless to write after 9, for example. And thanks for the revision! This is an important note - zayn1991

2 answers 2

Change the condition if (phone.length() >= 7 && !backspacingFlag) to if (phone.length() >= 9 && !backspacingFlag) and

 String ans = "+7(" + phone.substring(1, 4) + ") " + phone.substring(4,7) + "-" + phone.substring(7); 

Change to:

 String ans = "+7(" + phone.substring(1, 4) + ") " + phone.substring(4,7) + "-" + phone.substring(7,9) + "-" + phone.substring(9); 
  • It seems to me that at the end it should be: + "-" + phone.substring (7.9) + "-" + phone.substring (9); - iramm
  • @ iFr0z, the second dash has appeared, however, your line cuts off the last character and adds the numbers entered earlier. Check, please - zayn1991
  • @ iFr0z, c 7th in the condition issued a java.lang.StringIndexOutOfBoundsException: length=7; regionStart=7; regionLength=2 java.lang.StringIndexOutOfBoundsException: length=7; regionStart=7; regionLength=2 java.lang.StringIndexOutOfBoundsException: length=7; regionStart=7; regionLength=2 - zayn1991
  • @ zayn1991 and now everything works fine?) - iFr0z
  • @iramm thanks, didn't notice) - iFr0z

There is a small joint if you erase everything and start writing again.

It is treated by checking on +7

 if (phone.length() >= 4 && !backspacingFlag) { editedFlag = true; String beg=string.substring(0, 2); if ("+7".equals(beg)) { String ans = "+7(" + phone.substring(1, 4) + ") " + phone.substring(4); mEditTextPassword.setText(ans); mEditTextPassword.setSelection(mEditTextPassword.getText().length() - cursorComplement);} else { String ans = "+7(" + phone.substring(0, 3) + ") " + phone.substring(3); mEditTextPassword.setText(ans); mEditTextPassword.setSelection(mEditTextPassword.getText().length() - cursorComplement); } } 
  • I'll check it out now - zayn1991
  • one
    Your code will not work for those who want to erase everything and start writing from 8. If I enter in empty field from 89991112233, it will be transferred to +7 (999) 1112233, because many people intuitively write from 8-ki - zayn1991