web-dev-qa-db-ja.com

Text WatcherをトリガーせずにEditTextテキストを変更するにはどうすればよいですか?

EditTextフィールドにCustomer Text Watcherがあります。コードの一部で、.setText("whatever")を使用して行うEditTextの値を変更する必要があります。

問題は、その変更を行うとすぐに、無限ループを作成するafterTextChangedメソッドが呼び出されることです。 afterTextChangedをトリガーせずにテキストを変更するにはどうすればよいですか?

AfterTextChangedメソッドにテキストが必要なので、TextWatcherを削除することはお勧めしません。

82
user1143767

ウォッチャーの登録を解除してから再登録できます。

または、フラグを設定して、自分でテキストを変更したことをウォッチャーが認識できるようにすることもできます(したがって、無視する必要があります)。

60
CasualT

現在どのビューにフォーカスがあるかを確認して、ユーザーとプログラムによってトリガーされたイベントを区別できます。

_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 のように呼び出す必要があります。これにより、この問題も解決します。

88
Willi Mentzel
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()を使用している場合、テキストをすばやく入力するときに少し遅れを感じるかもしれません。

24
Chan Chun Him

簡単な修正方法... 新しい編集テキスト値を導出するロジックがべき等である限り(おそらくそうなるでしょうが、ただ言っているだけです)。リスナーメソッドでは、現在の値が前回値を変更した場合と異なる場合にのみ、編集テキストを変更します。

例えば。、

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

私はそのように使用します:

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);
3
Arthur Melo

こんにちは、EditTextテキストの変更に集中する必要がある場合は、フォーカスをリクエストできます。これは私のために働いた:

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

Kotlin DSL構文を使用して、これに対する一般的なソリューションを作成できます。

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

TextWatcher内では、次のように使用できます。

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

このロジックを試してください:私は無限ループに行くことなく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
        }
    });
1
ShAkKiR

これは私に適しています

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) { }
    });
1
user3361395

変更が発生したときに確認したい通常の場合に、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
1
JohnnyLambada

この問題は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
    }
}
1
Levon Petrosyan

私のバリアント:

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()をオーバーライドしたかったのですが、最終的なものです)

0
kandi

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);
}
0
Eurig Jones