27

How can I avoid that a code line like:

((EditText) findViewById(R.id.MyEditText)).setText("Hello");

Will cause an event here:

((EditText) findViewById(R.id.MyEditText)).addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start,
    int before, int count) {
// HERE
}

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

@Override
public void afterTextChanged(Editable s) {
}
});

I want to know if there is any way to inhibit the execution of onTextChanged as I noticed in the case of selecting a AutoCompleteTextView's dropdown result (no onTextChanged is executed!).

I'm not seeking for workarounds like "if hello do nothing"...

Halil Ozel
  • 2,482
  • 3
  • 17
  • 32
wildnove
  • 2,185
  • 2
  • 24
  • 32
  • 11
    Setting the text will trigger that listener. One simple way is to have a reference to the `TextWatcher`, remove the listener before setting the text(with `removeTextChangedListener(watcher)`), set the text, re enabled the listener(with `addTextChangedListener(watcher)`). You could have this in a method for convenience if you do it more than a few times. – user Oct 09 '12 at 14:40
  • possible duplicate of [Android: How can I change the EditText text without triggering the Text Watcher I have on it?](http://stackoverflow.com/questions/9385081/android-how-can-i-change-the-edittext-text-without-triggering-the-text-watcher) – kandi Aug 19 '15 at 18:31
  • Example of same question solved for update on checkbox with listener http://stackoverflow.com/a/15523518/2162226 – Gene Bo Feb 21 '17 at 22:46

6 Answers6

27

The Source for AutoCompleteTextView shows that they set a boolean to say the text is being replaced by the block completion

         mBlockCompletion = true;
         replaceText(convertSelectionToString(selectedItem));
         mBlockCompletion = false;    

This is as good a way as any to achieve what you want to do. The TextWatcher then checks to to see if the setText has come via a completion and returns out of the method

 void doBeforeTextChanged() {
     if (mBlockCompletion) return;

Adding and removing the TextWatcher will be more time consuming for the application

Dazzy_G
  • 820
  • 9
  • 22
  • I tried editing this question but apparently the edit queue is full. Long story short, if you're using an `AutoCompleteTextView` you can call `setText(CharSequence text, boolean filter)` and by passing the `filter` as `false`, you'll avoid triggering the `TextWatcher`. – Aleks Nine Aug 12 '21 at 17:13
15

You can check which View has the focus currently to distinguish between user and program triggered events.

EditText myEditText = (EditText) findViewById(R.id.myEditText);

myEditText.addTextChangedListener(new TextWatcher() {
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

        if(myEditText.hasFocus()) {
            // is only executed if the EditText was directly changed by the user
        }
    }

    //...
});

Take a look here for a more detailled version of that answer.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
6

Alternatively you can simply use mEditText.hasFocus() to distinguish between Text that human-changed or program-changed, this works fine for me:

@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
    if (mEditText.hasFocus()) {
        // Do whatever
    }
}

Hope this helps!

Allan Pereira
  • 2,572
  • 4
  • 21
  • 28
Jiyeh
  • 5,187
  • 2
  • 30
  • 31
3

An alternative way to achieve this would be to use setOnKeyListener.

This will only trigger when the user presses a key rather than whenever the EditText is changed by the user OR programmatically.

myEditText.setOnKeyListener(new EditText.OnKeyListener() {
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        switch(event.getAction()) {
            case KeyEvent.ACTION_DOWN:
                // beforeTextChanged
                break;      
            case KeyEvent.ACTION_UP:
                // afterTextChanged
                break;
        }
        return false;
    }
});
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Rockvole
  • 4,422
  • 3
  • 21
  • 27
2

I don't think there is an easy way to disable the listener, but you can get around it by either:

  • Removing the TextWatcher before you set the text, and adding it back after.
  • Or set a boolean flag before you set the text, which tells the TextWatcher to ignore it.

E.g.

boolean ignoreNextTextChange = true;
((EditText) findViewById(R.id.MyEditText)).setText("Hello");

And in your TextWatcher:

new TextWatcher() {
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        /*
        If the flag is set to ignore the next text change,
        reset the flag, and return without doing anything.
        */
        if (ignoreNextTextChange) {
            ignoreNextTextChange = false;
            return;
        }
    }

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

    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

It's a little hacky, but it should work :)

jox
  • 2,218
  • 22
  • 32
athor
  • 6,848
  • 2
  • 34
  • 37
  • Will returning there prevent execution of before and after text changed too, or would you need another boolean check there? – Menasheh Jul 15 '16 at 21:19
0

Only two ways I can see

  • you check within the listener
  • you add/remove the listener according to certain conditions

As the first way is a unwanted workaround for you I am a afraid you have work around with the second way.

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
dorjeduck
  • 7,624
  • 11
  • 52
  • 66