Подтвердить что ты не робот

Как я могу изменить текст EditText без запуска Text Watcher?

У меня есть поле EditText с отслеживанием текста клиента. В куске кода мне нужно изменить значение в EditText, которое я использую, используя .setText("whatever").

Проблема заключается в том, как только я делаю это изменение, вызывает метод afterTextChanged, который создает бесконечный цикл. Как я могу изменить текст без его запуска afterTextChanged?

Мне нужен текст в методе afterTextChanged, поэтому не предлагайте удалить TextWatcher.

4b9b3361

Ответ 1

Вы можете отменить регистрацию наблюдателя, а затем перерегистрировать его.

В качестве альтернативы вы можете установить флаг, чтобы ваш наблюдатель знал, когда вы только что изменили текст самостоятельно (и поэтому его следует игнорировать).

Ответ 2

Вы можете проверить, какой вид в настоящее время имеет фокус, чтобы различать события, вызванные пользователем и программой.

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

    //...
});

Изменить: Как правильно указано LairdPleng, это не работает, если myEditText уже имеет фокус, и вы программно меняете текст. Поэтому перед вызовом myEditText.setText(...) вы должны вызвать myEditText.clearFocus() как Chack сказал, и это решает эту проблему.

Ответ 3

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 update
        et.removeTextChangedListener(this);

        // The trick to update text smoothly.
        s.replace(0, s.length(), "text");

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

Использование:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

Вы можете почувствовать небольшую задержку при быстром вводе текста, если вы используете editText.setText() вместо editable.replace().

Ответ 4

Легкий трюк, чтобы исправить... так как ваша логика для вывода нового значения текстового текста является идемпотентной (что, вероятно, было бы, а просто сказать). В вашем методе слушателя измените только текст редактирования, если текущее значение отличается от последнего измененного значения.

например.

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

Ответ 5

Привет, если вам нужно сосредоточиться на EditText изменении текста, вы можете запросить фокус. Это сработало для меня:

if (getCurrentFocus() == editText) {
    editText.clearFocus();
    editText.setText("...");
    editText.requestFocus();
}

Ответ 6

Я так использую:

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

Ответ 7

попробуйте эту логику: Я хотел установить setText (""), не обращаясь к бесконечному циклу, и этот код работает для меня. Я надеюсь, что вы можете изменить это, чтобы соответствовать вашему требованию

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

Ответ 8

При настройке текста внутри текстового замещающего прослушивателя может возникать бесконечный цикл. Если это так, возможно, лучше не устанавливать текст там, в первую очередь. Таким образом, вы решите проблему цикла, и нет причин для обхода или потребления события.

Ответ 9

Мой вариант:

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(), но он окончательный)

Ответ 10

Я создал абстрактный класс, который смягчает циклическую проблему, когда модификация 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);
}

Ответ 11

Здесь удобный класс, который обеспечивает более простой интерфейс, чем TextWatcher, для обычного случая, когда вы хотите видеть изменения по мере их возникновения. Он также позволяет игнорировать следующее изменение по мере запроса ОП.

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

Ответ 12

Вы можете использовать синтаксис Kotlin DSL, чтобы иметь общее решение для этого:

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

А внутри вашего TextWatcher вы можете использовать его как:

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

Ответ 13

Это хорошо работает для меня

EditText inputFileName; // = (EditText)findViewbyId(R.id...)
inputFileName.addTextChangedListener(new TextWatcher() {
        public void afterTextChanged(Editable s) {

            //unregistering for event in order to prevent infinity loop
            inputFileName.removeTextChangedListener(this);

            //changing input text
            String regex = "[^a-z0-9A-Z\\s_\\-]";
            String fileName = s.toString();
            fileName = fileName.replaceAll(regex, "");
            s.replace(0, s.length(), fileName); //here is setting new text

            Log.d("tag", "----> FINAL FILE NAME: " + fileName);

            //registering back for text changes
            inputFileName.addTextChangedListener(this);
        }

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

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

Ответ 14

Эта проблема может быть легко решена с помощью tag filed, и вам даже не нужно иметь дело с фокусом editText.

Установка текста и тега программно

editText.tag = "dummyTag"
editText.setText("whatever")
editText.tag = null

Проверка tag в onTextChanged

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    if (editText.tag == null) {
       // your code
    }
}

Ответ 15

Вы должны убедиться, что ваша реализация изменений текста стабильна и не меняет текст, если никаких изменений не требуется. Обычно это будет любой контент, который уже был через наблюдателя один раз.

Самая распространенная ошибка заключается в том, чтобы установить новый текст в соответствующем EditText или Редактируемый, даже если текст фактически не изменился.

Кроме того, если вы внесете изменения в Редактируемый, а не в какой-либо конкретный вид, вы можете легко загрузить своего наблюдателя, а также вы можете протестировать его отдельно от некоторых модульных тестов, чтобы убедиться, что он имеет желаемый результат.

Поскольку Editable - это интерфейс, вы могли бы даже использовать фиктивную реализацию, которая генерирует исключение RuntimeException, если вызывается какой-либо из его методов, которые пытаются изменить его содержимое при тестировании контента, который должен быть стабильным.