EditText
フィールドにCustomer Text Watcherがあります。コードの一部で、.setText("whatever")
を使用して行うEditTextの値を変更する必要があります。
問題は、その変更を行うとすぐに、無限ループを作成するafterTextChanged
メソッドが呼び出されることです。 afterTextChangedをトリガーせずにテキストを変更するにはどうすればよいですか?
AfterTextChangedメソッドにテキストが必要なので、TextWatcher
を削除することはお勧めしません。
ウォッチャーの登録を解除してから再登録できます。
または、フラグを設定して、自分でテキストを変更したことをウォッチャーが認識できるようにすることもできます(したがって、無視する必要があります)。
現在どのビューにフォーカスがあるかを確認して、ユーザーとプログラムによってトリガーされたイベントを区別できます。
_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()
を Chack のように呼び出す必要があります。これにより、この問題も解決します。
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));
editable.replace()の代わりにeditText.setText()を使用している場合、テキストをすばやく入力するときに少し遅れを感じるかもしれません。
簡単な修正方法... 新しい編集テキスト値を導出するロジックがべき等である限り(おそらくそうなるでしょうが、ただ言っているだけです)。リスナーメソッドでは、現在の値が前回値を変更した場合と異なる場合にのみ、編集テキストを変更します。
例えば。、
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);
}
}
};
私はそのように使用します:
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
テキストの変更に集中する必要がある場合は、フォーカスをリクエストできます。これは私のために働いた:
if (getCurrentFocus() == editText) {
editText.clearFocus();
editText.setText("...");
editText.requestFocus();
}
Kotlin DSL構文を使用して、これに対する一般的なソリューションを作成できます。
fun TextView.applyWithDisabledTextWatcher(textWatcher: TextWatcher, codeBlock: TextView.() -> Unit) {
this.removeTextChangedListener(textWatcher)
codeBlock()
this.addTextChangedListener(textWatcher)
}
TextWatcher内では、次のように使用できます。
editText.applyWithDisabledTextWatcher(this) {
text = formField.name
}
このロジックを試してください:私は無限ループに行くことなく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
}
});
これは私に適しています
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's 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) { }
});
変更が発生したときに確認したい通常の場合に、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
この問題はtag
ファイルを使用して簡単に解決でき、editTextのフォーカスを処理する必要さえありません。
プログラムでテキストとタグを設定する
editText.tag = "dummyTag"
editText.setText("whatever")
editText.tag = null
onTextChangedのtag
の確認
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (editText.tag == null) {
// 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()をオーバーライドしたかったのですが、最終的なものです)
TextWatcherを介してEditTextに変更が加えられるという周期的な問題を軽減する抽象クラスを作成しました。
/**
* 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);
}