如何避免在Android的EditText中触发onTextChanged方法的执行?

27

我该如何避免类似以下代码行的问题:

((EditText) findViewById(R.id.MyEditText)).setText("Hello");

这里将会触发一个事件:

((EditText) findViewById(R.id.MyEditText)).addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start,
    int before, int count) {
// HERE
}

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

@Override
public void afterTextChanged(Editable s) {
}
});

我想知道是否有办法抑制onTextChanged的执行,因为我注意到在选择AutoCompleteTextView的下拉结果时(不执行onTextChanged!)。

我不是在寻找诸如“如果出现某种情况则不执行”的变通方法...


11
设置文本将触发该侦听器。一种简单的方法是拥有一个对TextWatcher的引用,在设置文本之前删除侦听器(使用removeTextChangedListener(watcher)),然后设置文本,在重新启用侦听器(使用addTextChangedListener(watcher))。如果您需要多次执行此操作,则可以将其放入一个方法中以提高方便性。 - user
使用监听器更新复选框的示例问题解决方案:https://dev59.com/uWUp5IYBdhLWcg3wGEgR#15523518 - Gene Bo
6个回答

27

AutoCompleteTextView 的源代码中可以看到,他们设置了一个布尔值来表示文本正在被块完成替换。

         mBlockCompletion = true;
         replaceText(convertSelectionToString(selectedItem));
         mBlockCompletion = false;    

这是实现你想要的功能的一种不错的方法。然后,TextWatcher检查是否通过完成来设置setText,并从方法中返回。

 void doBeforeTextChanged() {
     if (mBlockCompletion) return;

添加和移除TextWatcher会更加耗费应用程序的时间。


我尝试编辑这个问题,但显然编辑队列已满。 长话短说,如果你正在使用AutoCompleteTextView,你可以调用setText(CharSequence text, boolean filter),通过将filter设置为false,你将避免触发TextWatcher - Aleks Nine

15

您可以检查当前具有焦点的视图,以区分用户触发的事件和程序触发的事件。

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

    //...
});

在这里查看更详细的答案。


很棒的解决方案,我没有想到过。 - DIRTY DAVE

6

或者你可以简单地使用mEditText.hasFocus()来区分人为更改或程序更改的文本,这对我很有效:

@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
    if (mEditText.hasFocus()) {
        // Do whatever
    }
}

希望这能帮助您!

1
我不明白它如何有帮助。 - user9599745

3

另一种实现这个功能的方法是使用setOnKeyListener

这只会在用户按下键盘时触发,而不是每当用户或通过编程方式更改EditText时触发。

myEditText.setOnKeyListener(new EditText.OnKeyListener() {
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        switch(event.getAction()) {
            case KeyEvent.ACTION_DOWN:
                // beforeTextChanged
                break;      
            case KeyEvent.ACTION_UP:
                // afterTextChanged
                break;
        }
        return false;
    }
});

有趣的想法,但它能捕捉到用户将文本粘贴到字段中所做的文本更改吗? - jk7
1
用户倾向于绕过按钮,直接点击或进入下一个单元格。 - Jeffrey

2

我认为没有简单的方法可以禁用监听器,但您可以通过以下方式绕过它:

  • 在设置文本之前删除TextWatcher,然后在添加回来。
  • 或者在设置文本之前设置一个boolean标志,告诉TextWatcher忽略它。

例如:

boolean ignoreNextTextChange = true;
((EditText) findViewById(R.id.MyEditText)).setText("Hello");

在您的TextWatcher中:
new TextWatcher() {
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        /*
        If the flag is set to ignore the next text change,
        reset the flag, and return without doing anything.
        */
        if (ignoreNextTextChange) {
            ignoreNextTextChange = false;
            return;
        }
    }

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

    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

"

虽然有些瑕疵,但应该能够工作 :)

"

返回那里会阻止执行之前和之后的文本更改吗,还是需要在那里进行另一个布尔检查? - Menasheh

0

我只能看到两种方法:

  • 在侦听器内部检查
  • 根据某些条件添加/删除侦听器

由于第一种方式对您来说是一个不需要的解决方法,因此我很抱歉您必须使用第二种方式进行解决。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接