¿Cómo puedo cambiar el texto EditText sin activar el Observador de texto?


Tengo un campo EditText con un Observador de Texto del Cliente en él. En un fragmento de código necesito cambiar el valor en el EditText que hago usando .setText("whatever").

El problema es que tan pronto como hago ese cambio se llama al método afterTextChanged que creó un bucle infinito. ¿Cómo puedo cambiar el texto sin que se active afterTextChanged?

Necesito el texto en el método afterTextChanged, así que no sugiera eliminar el TextWatcher.

Author: Willi Mentzel, 2012-02-22

12 answers

Puede anular el registro del observador y luego volver a registrarlo.

Alternativamente, puede establecer una bandera para que su observador sepa cuándo acaba de cambiar el texto usted mismo (y por lo tanto debería ignorarlo).

 47
Author: CasualT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-21 21:04:21

Puede comprobar qué vista tiene actualmente el foco para distinguir entre los eventos activados por el usuario y el programa.

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

myEditText.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (getCurrentFocus() == myEditText) {
            // is only executed if the EditText was directly changed by the user
        }
    }

    //...
});

Edit: Como LairdPleng mencionado correctamente, esto no funciona si el myEditText ya tiene el foco y usted cambia el texto programáticamente. Por lo tanto, antes de llamar myEditText.setText(...) debe llamar myEditText.clearFocus() como dijo Chack, lo que resuelve este problema también.

 61
Author: Willi Mentzel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-31 10:32:40

Truco Fácil de arreglar ... siempre que su lógica para derivar el nuevo valor de texto de edición sea idempotente (lo cual probablemente sería, pero solo diciendo). En su método de escucha, solo modifique el texto de edición si el valor actual es diferente al de la última vez que modificó el valor.

Por ejemplo,

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);
    }
  }
};
 11
Author: Jeffrey Blattman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-04 00:15:06
public class MyTextWatcher implements TextWatcher {
    private EditText et;

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

    @Override
    public void afterTextChanged(Editable s) {
        // Unregister self before setText
        et.removeTextChangedListener(this);

        et.setText("text");

        // Re-register self after setText
        et.addTextChangedListener(this);
    }
}

Uso:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

Actualización:

Para actualizar el texto sin problemas, reemplace

et.setText("text");

Con

s.replace(0, s.length(), "text");
 7
Author: Chan Chun Him,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-28 05:25:59

Hola si necesitas mantenerte enfocado en EditText cambiar texto, puedes solicitar enfoque. Esto funcionó para mí:

if (getCurrentFocus() == editText) {
    editText.clearFocus();
    editText.setText("...");
    editText.requestFocus();
}
 3
Author: Milos Simic Simo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-02 22:21:21

Prueba esta lógica: Quería setText("") sin ir al bucle infinito y este código funciona para mí. Espero que pueda modificar esto para adaptarse a su requisito

        final EditText text= (EditText)findViewById(R.id.text);
        text.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(s.toString().isEmpty())return;
            text.setText("");
            //your code
        }
    });
 1
Author: ShAkKiR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-31 18:54:07

Mi variante:

public class CustomEditText extends AppCompatEditText{
    TextWatcher l;

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setOnTextChangeListener(TextWatcher l) {
        try {
            removeTextChangedListener(this.l);
        } catch (Throwable e) {}
        addTextChangedListener(l);
        this.l = l;
    }

    public void setNewText(CharSequence s) {
        final TextWatcher l = this.l;
        setOnTextChangeListener(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) {

            }
        });
        setText(s);
        post(new Runnable() {
            @Override
            public void run() {
                setOnTextChangeListener(l);
            }
        });
    }


}

Establecer oyentes solo usando setOnTextChangeListener() y establecer texto solo usando setNewText (quería anular setText(), pero es final)

 0
Author: danpetruk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 08:23:11

Puede producirse un bucle infinito al configurar el texto desde el oyente text changed. Si este es el caso, tal vez sea mejor no establecer el texto desde allí en primer lugar. Así que resolverás el problema del bucle, y no hay razón para omitir o consumir el evento.

 0
Author: auval,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-20 23:19:12

He creado una clase abstracta que mitiga el problema cíclico de cuando se realiza una modificación al EditText a través de un TextWatcher.

/**
 * An extension of TextWatcher which stops further callbacks being called as a result of a change
 * happening within the callbacks themselves.
 */
public abstract class EditableTextWatcher implements TextWatcher {

    private boolean editing;

    @Override
    public final void beforeTextChanged(CharSequence s, int start, int count, int after) {
        if (editing)
            return;

        editing = true;
        try {
            beforeTextChange(s, start, count, after);
        } finally {
            editing = false;
        }
    }

    abstract void beforeTextChange(CharSequence s, int start, int count, int after);

    @Override
    public final void onTextChanged(CharSequence s, int start, int before, int count) {
    if (editing)
        return;

        editing = true;
        try {
            onTextChange(s, start, before, count);
        } finally {
            editing = false;
        }
    }

    abstract void onTextChange(CharSequence s, int start, int before, int count);

    @Override
    public final void afterTextChanged(Editable s) {
        if (editing)
            return;

        editing = true;
        try {
            afterTextChange(s);
        } finally {
            editing = false;
        }
    }    

    public boolean isEditing() {
        return editing;
    }

    abstract void afterTextChange(Editable s);
}
 0
Author: Eurig Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 13:05:43

Aquí hay una clase útil que proporciona una interfaz más simple que TextWatcher para el caso normal de querer ver los cambios a medida que ocurren. También permite ignorar el siguiente cambio como el OP solicitado.

public class EditTexts {
    public final static class EditTextChangeListener implements TextWatcher {
        private final Consumer<String> onEditTextChanged;
        private boolean ignoreNextChange = false;
        public EditTextChangeListener(Consumer<String> onEditTextChanged){
            this.onEditTextChanged = onEditTextChanged;
        }
        public void ignoreNextChange(){
            ignoreNextChange = true;
        }
        @Override public void beforeTextChanged(CharSequence __, int ___, int ____, int _____) { }
        @Override public void onTextChanged(CharSequence __, int ___, int ____, int _____) { }
        @Override public void afterTextChanged(Editable s) {
            if (ignoreNextChange){
                ignoreNextChange = false;
            } else {
                onEditTextChanged.accept(s.toString());
            }
        }
    }
}

Úsalo así:

EditTexts.EditTextChangeListener listener = new EditTexts.EditTextChangeListener(s -> doSomethingWithString(s));
editText.addTextChangedListener(listener);

Siempre que desee modificar el contenido de editText sin causar una cascada de ediciones recursivas, haga lo siguiente:

listener.ignoreNextChange();
editText.setText("whatever"); // this won't trigger the listener
 0
Author: JohnnyLambada,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-27 01:43:01

Yo uso de esa manera:

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);
                }
            }
        });

Y cada vez que necesite cambiar el texto programáticamente, primero borre el foco

mEditText.clearFocus();
mEditText.setText(lastAddress.complement);
 0
Author: Arthur Melo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-07 19:41:59

Debe asegurarse de que su implementación de cambios de texto sea estable y no cambie el texto si no se necesita ningún cambio. Normalmente sería cualquier contenido que ya haya pasado por el observador una vez.

El error más común es establecer un nuevo texto en el EditText asociado o en el Editable aunque el texto no haya sido realmente modificado.

Además de eso, si realiza sus cambios en la vista Editable en lugar de alguna Vista específica, puede volver a utilizar fácilmente su observador, y también puede probarlo de forma aislada con algunas pruebas unitarias para asegurarse de que tenga el resultado que desea.

Dado que Editable es una interfaz, incluso podría usar una implementación ficticia de la misma que arroja una excepción de RuntimeException si se llama a cualquiera de sus métodos que intente cambiar su contenido, al probar contenido que debería ser estable.

 -1
Author: thoutbeckers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 10:55:47