Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I change the EditText text without triggering the Text Watcher?

People also ask

How do I remove text from EditText?

just use the android:hint attribute in your EditText. This text shows up when the box is empty and not focused, but disappears upon selecting the EditText box. this should be the accepted answer, +1 for simplicity.

How do I get text in EditText?

Now, before we start we need to know some method of EditText which we use to get or fetch the text written in an EditText that is . getText() method and the other one is . setText() method that we use to set some pre-written text or string. EditText demo; demo=(EditText)findViewById(R.


Short answer

You can check which View currently has the focus 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
        }
    }

    //...
});

Long answer

As an addition to the short answer: In case myEditText already has the focus when you programmatically change the text you should call clearFocus(), then you call setText(...) and after you you re-request the focus. It would be a good idea to put that in a utility function:

void updateText(EditText editText, String text) {
    boolean focussed = editText.hasFocus();
    if (focussed) {
        editText.clearFocus();
    }
    editText.setText(text);
    if (focussed) {
        editText.requestFocus();
    }
}

For Kotlin:

Since Kotlin supports extension functions your utility function could look like this:

fun EditText.updateText(text: String) {
    val focussed = hasFocus()
    if (focussed) {
        clearFocus()
    }
    setText(text)
    if (focussed) {
        requestFocus()
    }
}

You could unregister the watcher, and then re-register it.

Alternatively, you could set a flag so that your watcher knows when you have just changed the text yourself (and therefore should ignore it).


Java:

public class MyTextWatcher implements TextWatcher {
    private EditText editText;

    // Pass the EditText instance to TextWatcher by constructor
    public MyTextWatcher(EditText editText) {
        this.editText = editText;
    }

    @Override
    public void afterTextChanged(Editable e) {
        // Unregister self before update
        editText.removeTextChangedListener(this);

        // The trick to update text smoothly.
        e.replace(0, e.length(), e.toString());

        // Re-register self after update
        editText.addTextChangedListener(this);
    }

    ...
}

Kotlin:

class MyTextWatcher(private val editText: EditText) : TextWatcher {

  override fun afterTextChanged(e: Editable) {
    editText.removeTextChangedListener(this)
    e.replace(0, e.length, e.toString())
    editText.addTextChangedListener(this)
  }

  ...
}

Usage:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

You may feel a little bit lag when entering text rapidly if you are using editText.setText() instead of editable.replace().


Easy trick to fix ... as long a your logic to derive the new edit text value is idempotent (which it probably would be, but just saying). In your listener method, only modify the edit text if the current value is different than the last time you modified the value.

e.g.,

TextWatcher tw = new TextWatcher() {
  private String lastValue = "";

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

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

  @Override
  public void afterTextChanged(Editable editable) {

    // Return value of getNewValue() must only depend
    // on the input and not previous state
    String newValue = getNewValue(editText.getText().toString());
    if (!newValue.equals(lastValue)) {
      lastValue = newValue;

      editText.setText(newValue);
    }
  }
};

You can use Kotlin DSL syntax to have the generic solution for this:

fun TextView.applyWithDisabledTextWatcher(textWatcher: TextWatcher, codeBlock: TextView.() -> Unit) {
    this.removeTextChangedListener(textWatcher)
    codeBlock()
    this.addTextChangedListener(textWatcher)
}

And inside your TextWatcher, you can use it as:

editText.applyWithDisabledTextWatcher(this) {
    text = formField.name
}

I use that way:

mEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

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

            @Override
            public void afterTextChanged(Editable s) {
                if (mEditText.isFocused()) { //<-- check if is focused 
                    mEditText.setTag(true);
                }
            }
        });

And every time you need to change text programatically, first clear the focus

mEditText.clearFocus();
mEditText.setText(lastAddress.complement);