Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use AsYouTypeFormatter TextWatcher in Android App?

I am trying to use the AsYouTypeFormatter from Google's libphonenumber with TextWatcher and am not sure if it's possible. I have been able to format the text as typed from an EditText field and output it in another EditText but not able to change the original EditText field directly (which is what I want). I know about the phoneNumberFormattingTextWatcher but we want the user to be able to eventually select which locale they're in and have more control than using that allows (from what I have gathered).

Top of class:

private View phoneNumberView;
private EditText phoneNumberText;
private String formattedPhoneNumber;
private boolean isInAfterTextChanged;
private AsYouTypeFormatter aytf;

First I initialize the AsYouTypeFormatter at the top of my class:

aytf = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(Locale.getDefault().getCountry());

After my tabs and such are created, I create an EditText box, format the existing phone number inside of it (which works), and attach the listener (which is the current class):

phoneNumberView = inflater.inflate(R.layout.phone_number_setting, null);
phoneNumberText = (EditText) phoneNumberView.findViewById(R.id.dirty_phone_number);
phoneNumberText.setText(dirtyPhoneNumber);
for (int i = 0; i < dirtyPhoneNumber.length(); i++){
    phoneNumberText.setText(aytf.inputDigit(dirtyPhoneNumber.charAt(i)));
}
aytf.clear();
phoneNumberText.setText(dirtyPhoneNumber);
phoneNumberText.addTextChangedListener(this);

Then this is what I have at the moment in my TextWatcher functions, I am not sure which one of these functions my code should be in though:

@Override
public void afterTextChanged(Editable s) {
    if (!isInAfterTextChanged) {
           isInAfterTextChanged = true;

           if(s.length() > 0){
               Log.v("AsYouTypeFormatter - source", s.toString());
               for(int i = 0; i < s.length(); i++){
                   formattedPhoneNumber = aytf.inputDigit(s.charAt(i));
                   Log.v("AsYouTypeFormatter - formatted", formattedPhoneNumber);

               }
               Log.v("AsYouTypeFormatter - source after loop", s.toString());
             //The formatted output shows properly in this EditText but not when I try to put it back into the original one (phoneNumberText) 
               EditText testPhoneNumberText = (EditText) phoneNumberView.findViewById(R.id.testPhoneNumberText);  
               testPhoneNumberText.setText(formattedPhoneNumber);
               aytf.clear();
           }

           formattedPhoneNumber = null;
           isInAfterTextChanged = false;
       }        

}

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

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

Here is a sample of the logged output, so I know the formatting is working:

01-03 10:55:02.838: V/AsYouTypeFormatter - source(27114): 15552451234
01-03 10:55:02.838: V/AsYouTypeFormatter - formatted(27114): 1
01-03 10:55:02.838: V/AsYouTypeFormatter - formatted(27114): 15
01-03 10:55:02.838: V/AsYouTypeFormatter - formatted(27114): 1 55
01-03 10:55:02.838: V/AsYouTypeFormatter - formatted(27114): 1 555
01-03 10:55:02.838: V/AsYouTypeFormatter - formatted(27114): 1 555-2
01-03 10:55:02.838: V/AsYouTypeFormatter - formatted(27114): 1 555-24
01-03 10:55:02.850: V/AsYouTypeFormatter - formatted(27114): 1 555-245
01-03 10:55:02.850: V/AsYouTypeFormatter - formatted(27114): 1 555-245-1
01-03 10:55:02.850: V/AsYouTypeFormatter - formatted(27114): 1 555-245-12
01-03 10:55:02.850: V/AsYouTypeFormatter - formatted(27114): 1 555-245-123
01-03 10:55:02.850: V/AsYouTypeFormatter - formatted(27114): 1 555-245-1234
01-03 10:55:02.850: V/AsYouTypeFormatter - source after loop(27114): 15552451234

And here is the image of the output (top EditText is phoneNumberText and bottom is testPhoneNumberText): http://imgur.com/GXwRu.png

What I want to know is how do I get the formatted output back into the original EditText as it's being typed? When I try to do it, weird things happen like duplication or it just shows it unformatted. I've tried using s.replace() but I am not sure that I am using it properly. Is this possible? Thanks?

like image 572
sugarwaffle Avatar asked Jan 03 '13 16:01

sugarwaffle


2 Answers

For others out there who just want to format a user-entered phone number in an EditText as the user types, it's much, much easier to use PhoneNumberFormattingTextWatcher (built in to Android) than attempt any of these verbose answers - and it's ONE LINE OF CODE!

//Add a special listener for this instance that will format phone numbers on the fly.
this.editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher());

You can also pass the region the user has selected, which I think would actually answer the OP's question, but it wasn't available until API 21:

//This version takes a country code!
this.editText.addTextChangedListener(new PhoneNumberFormattingTextWatcher("US"));
like image 149
DiscDev Avatar answered Nov 15 '22 09:11

DiscDev


I ended up declaring a new String at the top:

private String unformattedPhoneNumber;

Then changing my code to this:

@Override
public void afterTextChanged(Editable s) {
    if (!isInAfterTextChanged) {
       isInAfterTextChanged = true;

       if(s.length() > 0){
           Log.v("AsYouTypeFormatter - source", s.toString());
           unformattedPhoneNumber = s.toString().replaceAll("[^\\d.]", "");
           for(int i = 0; i < unformattedPhoneNumber.length(); i++){
               formattedPhoneNumber = aytf.inputDigit(unformattedPhoneNumber.charAt(i));
               Log.v("AsYouTypeFormatter - formatted", formattedPhoneNumber);

           }
           Log.v("AsYouTypeFormatter - source after loop", s.toString());

           phoneNumberText.setText(formattedPhoneNumber);
           aytf.clear();
       }

       formattedPhoneNumber = null;
       isInAfterTextChanged = false;
   }
}

It seems that the aytf wasn't able to format already partially formatted phone numbers so I had to strip out all non digits before resubmitting to aytf? The only problem left now is that the cursor in the EditText field is at the beginning instead of the end now, but that shouldn't be a problem to fix. Yay.

EDITED CODE:

@Override
public void afterTextChanged(Editable s) {
    if (!isInAfterTextChanged) {
        isInAfterTextChanged = true;
        phoneNumberText.setText(pnu.updateNationalNumber(s.toString()));
        phoneNumberText.setSelection(this.phoneNumberText.getText().length());
        isInAfterTextChanged = false;
    }
}

 /**
 * Updates the national number based on the param s
 * Takes all formatting out of param s and then reformats the number
 * using the AsYouTypeFormatter for libphonenumber and based upon
 * the region code
 *  
 * @param s The formatted value to be used to update the national number
 * @return String The new formatted national number
 */
public String updateNationalNumber(String s){
    //Instantiate the as you type formatter with the current region (US or UK)
    aytf = phoneUtil.getAsYouTypeFormatter(this.currentRegionCode.getCountryCode());
    String fNationalNumber = null;

    //Format the string
    if(s.length() > 0){
        String digitString = null;
        //If it's in the US remove all leading 1s (international code)
        if(this.currentRegionCode == RegionCode.US){
            digitString = new String(s.replaceAll("(^[1?])|([^\\d.])", ""));
        }
        //If it's in the UK remove all leading 44s (international code)
        else if (this.currentRegionCode == RegionCode.GB){
            digitString = new String(s.replaceAll("(^[4?]{2})|([^\\d.])", ""));
        }
       if(digitString != null){
           //RE input all of the digits into the formatter
           for(int i = 0; i < digitString.length(); i++){
               fNationalNumber = aytf.inputDigit(digitString.charAt(i));
           }
       }

       //Clear the formatter for the next round of input
       aytf.clear();

       //Try to update the phone number with the formatted number
       try {
           phoneUtil.parse(fNationalNumber, this.currentRegionCode.getCountryCode(), this.uPhoneNumber);
       //Rejects if the number isn't in an acceptable format for the region code given etc.
       } catch (NumberParseException e) {
          System.err.println("NumberParseException was thrown: " + e.toString());
       }
    }
    //Return the formatted phone number
    return fNationalNumber;
}
like image 27
sugarwaffle Avatar answered Nov 15 '22 08:11

sugarwaffle