Как я могу изменить текст EditText без запуска text Watcher?
у меня есть EditText поле с наблюдателем текста клиента на нем. В куске кода мне нужно изменить значение в EditText, который я использую .setText("whatever").
проблема в том, как только я сделаю это изменение afterTextChanged вызывается метод, который создал бесконечный цикл. Как я могу изменить текст, без его запуска afterTextChanged?
мне нужен текст в методе afterTextChanged, поэтому не предлагайте удалить TextWatcher.
12 ответов:
вы можете отменить регистрацию наблюдателя, а затем вновь зарегистрировать его.
кроме того, вы можете установить флаг, чтобы ваш наблюдатель знал, когда вы только что изменили текст самостоятельно (и поэтому должны игнорировать его).
вы можете проверить, какой вид в настоящее время имеет фокус, чтобы различать события, вызванные пользователем и программой.
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: как LairdPleng правильно сказали, это не работает, если
myEditTextуже имеет фокус, и вы программно изменить текст. Итак, перед вызовомmyEditText.setText(...)вы должны позвонитьmyEditText.clearFocus()как проверяем, сказал, что решает эту проблему.
легко исправить ... пока ваша логика для получения нового текстового значения редактирования является идемпотентной (что, вероятно, было бы, но просто говорю). В методе прослушивателя измените текст редактирования только в том случае, если текущее значение отличается от последнего изменения значения.
например,
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); } } };
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); } }использование:
et_text.addTextChangedListener(new MyTextWatcher(et_text));обновление:
обновить текст более плавно, замените
et.setText("text");С
s.replace(0, s.length(), "text");
Привет, если вам нужно сосредоточиться на
EditTextизменить текст, вы можете запросить фокус. Это сработало для меня:if (getCurrentFocus() == editText) { editText.clearFocus(); editText.setText("..."); editText.requestFocus(); }
попробуйте эту логику: Я хотел установить текст (""), не переходя к бесконечному циклу, и этот код работает для меня. Я надеюсь, что вы можете изменить это, чтобы соответствовать вашим требованиям
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 } });
мой вариант:
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); } }); } }установить прослушиватели только с помощью setOnTextChangeListener () и установить текст только с помощью setNewText (я хотел переопределить setText (), но это окончательно)
бесконечный цикл может возникнуть при установке текста из текстового измененного слушателя. Если это так, возможно, лучше не устанавливать текст оттуда в первую очередь. Таким образом, вы решите проблему цикла, и нет никаких причин, чтобы обойти или использовать событие.
Я создал абстрактный класс, который смягчает циклическую проблему, когда модификация EditText производится через 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); }
вот удобный класс, который обеспечивает более простой интерфейс, чем TextWatcher для обычного случая, когда вы хотите видеть изменения по мере их возникновения. Это также позволяет игнорировать следующее изменение по запросу OP.
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()); } } } }используйте его так:
EditTexts.EditTextChangeListener listener = new EditTexts.EditTextChangeListener(s -> doSomethingWithString(s)); editText.addTextChangedListener(listener);всякий раз, когда вы хотите изменить содержание
editTextне вызывая каскад рекурсивных изменений, сделайте следующее:listener.ignoreNextChange(); editText.setText("whatever"); // this won't trigger the listener
Я использую таким образом:
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); } } });и каждый раз, когда вам нужно изменить текст программно, сначала очистить фокус
mEditText.clearFocus(); mEditText.setText(lastAddress.complement);
вы должны убедиться, что ваша реализация текстовых изменений является стабильной и не изменяет текст, если никаких изменений не требуется. Обычно это будет любой контент, который уже был через наблюдателя один раз.
наиболее распространенной ошибкой является установка нового текста в связанный EditText или редактируемый, даже если текст не был фактически изменен.
кроме того, если вы внесете свои изменения в редактируемый вместо определенного представления, вы можете легко повторно использовать свой watcher, а также вы можете протестировать его в изоляции с помощью некоторых модульных тестов, чтобы убедиться, что он имеет желаемый результат.
поскольку Editable-это интерфейс, вы можете даже использовать его фиктивную реализацию, которая вызывает исключение RuntimeException, если какой-либо из его методов вызван, который пытается изменить его содержимое, при тестировании содержимого, которое должно быть стабильным.
Comments