4

I have an EditText and a TextWatcher.

Skeleton of my code:

EditText x;
x.addTextChangedListener(new XyzTextWatcher());

XyzTextWatcher implements TextWatcher() {
    public synchronized void afterTextChanged(Editable text) {
        formatText(text);
    }
}

My formatText() method inserts some hyphens at some positions of the text.

private void formatText(Editable text) {
    removeSeparators(text);

    if (text.length() >= 3) {
        text.insert(3, "-");
    }
    if (text.length() >= 7) {
        text.insert(7, "-");
    }
}

private void removeSeparators(Editable text) {
    int p = 0;
    while (p < text.length()) {
        if (text.charAt(p) == '-') {
            text.delete(p, p + 1);
        } else {
            p++;
        }
    }
}

The problem I have is - what is displayed on my EditText isn't in sync with the Editable. When I debugged the code, I saw that the variable text (Editable) has the expected value, but what's shown on the EditText doesn't always match the Editable.

For example, when I have a text x = "123-456-789" I cut the text "456" from x manually. After formatting, my Editable has the value "123-789-" However, the value shown on my EditText is "123--789"

They have the same value in most cases though.

I assumed that the EditText IS the Editable and they should always match. Am I missing something?

Sam
  • 7,252
  • 16
  • 46
  • 65
Karthz
  • 53
  • 1
  • 2
  • 7

1 Answers1

5

Ok, you never actually change the EditText just the Editable. Android EditTexts are not children of the Editable class. Strings are subclasses of the Editable class. The onTextChangedListener doesn't receive the EditText as an argument but the Editable/String displayed in the EditText. After you format the Editable with the hyphens you then need to update the EditText. Something like this should work fine:

class MyClass extends Activity{

    //I've ommited the onStart(), onPause(), onStop() etc.. methods

    EditText x;
    x.addTextChangedListener(new XyzTextWatcher());

    XyzTextWatcher implements TextWatcher() {
        public synchronized void afterTextChanged(Editable text) {
            String s = formatText(text);
            MyClass.this.x.setText(s);
        }
    }

}

To prevent the slowdown why not change the formatText method something like this?

private Editable formatText(Editable text) {
    int sep1Loc = 3;
    int sep2Loc = 7;

    if(text.length==sep1Loc)
    text.append('-');

    if(text.length==sep2Loc)
    text.append('-');

    return text;
}

Note: I haven't tested this

slayton
  • 20,123
  • 10
  • 60
  • 89
  • Thanks. I tried that as well, and it gave me the expected result. However, it significantly slowed down the input. I had to wait a bit to input every digit. So, I thought that the idea wasn't correct. – Karthz Sep 15 '11 at 19:09
  • That isn't surprising because every time you enter in a new digit the listener gets called. You really don't need to reformat the string every time a digit is entered, only when the string reaches certain lenghts.Try putting an if clause at the beginning of the formatText() method that immediately returns in the string is too short or isn't the right length. – slayton Sep 15 '11 at 19:12
  • Great, thanks. I will try that and see if it makes it any faster. – Karthz Sep 15 '11 at 19:14
  • No, that wouldn't help my case since no hyphens would be inserted when I paste a number into the EditText. afterTextChanged() would be called only once for a paste. Not for each digit. Thanks. – Karthz Sep 15 '11 at 21:07
  • Ok, that wasn't meant as a perfect replacement rather as an example of simpler implementation – slayton Sep 15 '11 at 21:24
  • Yup, I understand. :) Thank you for all the quick responses. It works pretty well now. – Karthz Sep 16 '11 at 07:27
  • 4
    How did you get this to work without a stack overflow due to afterTextChanged being called again? – chucky Dec 19 '11 at 22:00
  • @chucky its probably best that you just ask a new question with your code. You'll get more responses. – slayton Dec 19 '11 at 22:14
  • 1
    do you not need to remove and re-add the textWatcher in the afterTextChanged method? I'd think this would stack overflow like @chucky said. – loeschg Nov 01 '13 at 17:37
  • 2
    Actually you don't need to update the EditText, you just need to edit Editable! If your changes isn't accepted, try to check InputFilters, that are set in that Editable. Filters may ignore your changes. Means Editable.get/setFilters. – demaksee Sep 01 '16 at 07:51
  • And another idea: use some boolean flag to prevent recursive call of afterTextChanged. There is such answer: http://stackoverflow.com/a/28865522/755313 – demaksee Sep 01 '16 at 07:53
  • Using EditText.setText will have some side effect, like removing the caps lock. Is there any way to prevent that? – Gyome May 18 '17 at 09:20