テキストウォッチャーを起動せずにEditTextテキストを変更するにはどうすればよいですか?


104

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

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

afterTextChangedメソッドにテキストが必要なので、を削除しないでくださいTextWatcher

回答:


70

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

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


あなたのヒントは私には十分です。実際、私はonResumeにリスナーを登録していましたが、onPause()には登録を解除していなかったため、複数回呼び出されていました。
Smeet 2017年

誰かがそれを説明できますか?技術的に、私はアンドロイドの新人です、もっと詳細なplsが必要です、ありがとう。
Budi Mulyo

116

短い答え

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

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

myEditText.addTextChangedListener(new TextWatcher() {

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

    //...
});

長い答え

短い答えへの追加として:myEditTextプログラムで呼び出すテキストを変更するときに既にフォーカスがある場合は、clearFocus()を呼び出しsetText(...)、フォーカスを再度要求した後に呼び出します。それをユーティリティ関数に入れるのは良い考えです:

void updateText(EditText editText, String text) {
    boolean focussed = editText.hasFocus();
    if (focussed) {
        editText.clearFocus();
    }
    editText.setText(text);
    if (focussed) {
        editText.requestFocus();
    }
}

Kotlinの場合:

Kotlinは拡張関数をサポートしているため、ユーティリティ関数は次のようになります。

fun EditText.updateText(text: String) {
    val focussed = hasFocus()
    if (focussed) {
        clearFocus()
    }
    setText(text)
    if (focussed) {
        requestFocus()
    }
}

断片的getActivity().getCurrentFocus()でコトリンactivity?.currentFocus
Mohammad Reza Khahani

@mohammadrezaKhahaniねえ!あなたはそれを必要としません。でhasFocus()直接呼び出すことができますEditText
Willi Mentzel、

理想的ではありません。edittextにフォーカスがある場合でも、トリガーリスナーなしでsetText()を使用するにはどうすればよいですか?
Jaya Prakash

31
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


なぜこれは反対票が投じられたのですか?他の人がうまくいかなかったのに対して、これは私の問題の完璧な解決策です。ただし、このソリューションが機能するためにTextWatcherをサブクラス化する必要はありません。Chan Chun Himに感謝します。
Michael Fulton

TextWatcherの登録を解除して再登録するというあなたのアイデアはうまくいきます。ただし、et.setText( "")は機能しますが、s.replace(0、s.length()、 "")は機能しません。
stevehs17 2017

@ stevehs17、私はあなたのコードがどうなのか本当にわかりませんが、使用法はかなり詳細に更新されました、試してみてください。
Chan Chun Him

15

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

例えば、

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

1
これにより、メソッドが2回呼び出されることになりますが、違いますか?
Trevor Hart、

はい、それが私がべき等であるべきであると述べた理由です(それをより明確にする必要があります)...理想的ではありませんが、機能しない単一のメソッド呼び出しのオーバーヘッドが心配な場合は、ハードコア最適化を行う必要があります。その場合は、コードを再編成して、リスナーを呼び出す前に削除し、setText()後で追加し直すことができます。
ジェフリーBlattman

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

5

Kotlin DSL構文を使用して、この一般的なソリューションを実現できます。

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

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

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

ずっときれい。Kotlin DSLはすばらしい
Anjal Saneen

4

これは私にとってはうまくいきます

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

3

この問題は、tagfiledを使用して簡単に解決でき、editTextのフォーカスに対処する必要さえありません。

プログラムによるテキストとタグの設定

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

tagin onTextChangedの確認

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

3

EditText変更テキストに集中する必要がある場合は、フォーカスを要求できます。

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

1

このロジックを試してください:無限ループに行かずに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

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

0

私の亜種:

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

EditWatchへの変更が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

非常にシンプルで、この方法でテキストを設定します

void updateText(EditText et, String text) {
   if (!et.getText().toString().equals(text))
       et.setText(text);
}

-2

テキストの変更の実装が安定していて、変更が必要ない場合はテキストを変更しないようにする必要があります。通常、これは、一度ウォッチャーを通過したコンテンツです。

最も一般的な間違いは、テキストが実際には変更されていなくても、関連するEditTextまたはEditableに新しいテキストを設定することです。

さらに、特定のビューの代わりに編集可能に変更を加えると、ウォッチャーを簡単に再利用できます。また、いくつかの単体テストを使用して、ウォッチャーを分離してテストし、目的の結果が得られることを確認できます。

Editableはインターフェイスであるため、安定しているはずのコンテンツをテストするときに、コンテンツを変更しようとするメソッドが呼び出された場合、RuntimeExceptionをスローするダミーの実装を使用することもできます。


-2

物事を行う私の方法:

書き込みセグメント

        EditText e_q;

        e_q = (EditText) parentView.findViewWithTag("Bla" + i);

        int id=e_q.getId();
        e_q.setId(-1);
        e_q.setText("abcd...");
        e_q.setId(id);

リスナー

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

        int id = view.getId();
        if(id==-1)return;

        ....

とにかく動作します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.