关于android:TextWatcher事件被多次触发

TextWatcher events are being fired multiple times

我对TextWatcher有一个烦人的问题。我一直在网上搜索,但找不到任何东西。感谢有人可以帮助我。

由于某种原因,一次文本更改后对TextWatcher事件的调用是不稳定的。有时它们会被触发一次(应该如此),有时会被触发两次,有时会被触发3次。不知道为什么,整个事情非常简单。也有时afterTextChanged()上的Editable参数在toString()和length()中返回空值。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
    private TextWatcher mSearchAddressTextChangeListener = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) { }

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

        @Override
        public void afterTextChanged(Editable searchedAddress) {
           System.out.println("called multiple times.");  
        }
    };

afterTextChanged()(和AsyncTask)内部,不对文本或EditText视图进行任何更改。

我看到在TextWatcher的事件中提出的问题被调用了两次,但是即时事件触发的次数多于(或少于)两次。

无论如何,感谢任何帮助。

编辑:我删除了afterTextChanged()的内容,即使没有我的代码,也会出现此问题。是什么让我相信这是一个错误。当在常规字符之后立即输入"空格"字符(两次触发事件处理程序)或删除常规字符之后的"空格"字符(退格。事件处理程序被触发3次)时,就会发生此错误。帮助仍将不胜感激。


我遇到了同样的问题,当我在连续文本的末尾用光标按退格键时,afterTextChange被调用了3次:
-第一次使用正确的s值
-第二次具有明确的价值
-第三次输入正确的值

在网上搜索了很多之后,我尝试将EditText inputType更改为

1
android:inputType="textNoSuggestions"

不要问我为什么,但是它起作用了,afterTextChanged现在仅被调用一次。


根据TextWatcher的开发人员页面,如果对TextWatcher中的Editable进行了更改,则将触发对链接到该Editable的所有TextWatchers的进一步调用。现在,很明显,您的代码不会触发此行为。

但是,很有可能由于某种原因,如果系统在Editable上具有TextWatcher,则可能会出现您描述的情况。"为什么",我听到你在哭,"这会发生吗?"

首先,经典防御:没有理由不发生这种防御,严格来说,应编写应用程序代码以使其具有弹性。

其次,我无法证明这一点,但是我可以很好地想象一下,处理EditText中显示文本布局的代码使用了TextWatcher来处理更新屏幕上文本的显示。该代码可能会将控制代码(未显示)插入Editable中,以确保良好的换行符等。它甚至可能会绕过几次循环才能正确处理,并且您可能只有在它完成所有工作后才收到第一个电话。

编辑

根据@Learn OpenGL ES的评论,对于诸如自动更正之类的事情,调用TextWatcher是正常的。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean isOnTextChanged = false;

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

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
    isOnTextChanged = true;
}

@Override
public void afterTextChanged(Editable quantity) {
    if (isOnTextChanged) {
        isOnTextChanged = false;
       //dosomething
    }


我尝试了所有关于此问题的解决方案,但没有一个对我有用。但是经过一番搜索,我发现了这篇文章。使用RxJava进行反跳对我来说效果很好。这是我的最终解决方案:

在Gradle文件中添加RxJava依赖项:

1
2
3
compile 'io.reactivex:rxandroid:1.0.1'
compile 'io.reactivex:rxjava:1.0.14'
compile 'com.artemzin.rxjava:proguard-rules:1.0.14.2'

实施您的主题:

1
2
3
4
5
6
PublishSubject<String> yourSubject = PublishSubject.create();
    yourSubject .debounce(100, TimeUnit.MILLISECONDS)
            .onBackpressureLatest()
            .subscribe(s -> {
                //Implements anything you want
            });

在TextWatcher中使用您的主题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TextWatcher myTextWatcher = 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) {
        yourSubject.onNext(s.toString()); //apply here your subject
    }

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

在EditText侦听器中添加TextWatcher:

1
my_edit_text.addTextChangedListener(myTextWatcher);


您可以使用布尔检查,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    inputBoxNumberEt.addTextChangedListener(new TextWatcher() {

        boolean ignoreChange = false;

        @Override
        public void afterTextChanged(Editable s) {
        }

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

        @Override
        public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
            if (!ignoreChange) {
              ///Do your checks                    
                ignoreChange = true;
                inputBoxNumberEt.setText(string);
                inputBoxNumberEt.setSelection(inputBoxNumberEt.getText().length());
                ignoreChange = false;
            }
        }
    });

对我来说,以下代码有效。如果afterTextChanged方法中发生条件循环,请注意布尔值已更改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        edittext.addTextChangedListener(new TextWatcher()
        {
            boolean considerChange = false;

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

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

            public void afterTextChanged(Editable editable)
            {
                if (considerChange)
                {
                    // your code here
                }
                considerChange = !considerChange; //see that boolean value is being changed after if loop
            }


        });

我的问题与此有关,因为我在模拟器中运行应用程序,并通过笔记本电脑的键盘打字(不使用模拟器的键盘),并且奇数输入将被复制。

这不是在物理设备中发生的。

从Nico的上述回答中得到了一些启发,这对我来说使输入文本更改仅触发一次就起作用了。

添加了以下输入类型配置:

1)通过风格

1
2
3
4
<!-- in a style -->
<style name="My.Text.Style">
 <item name="android:inputType">text|textMultiLine|textVisiblePassword</item>
</style>

然后在xml中用作:

1
2
3
4
5
6
<EditText
   android:id="@+id/my_edit_text"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   style="@style/My.Text.Style"
   />

要么

2)直接进入xml

1
2
3
4
<EditText
   android:id="@+id/my_edit_text"
   android:inputType="text|textMultiLine|textVisiblePassword"
   />

两种方法都应该很好,这种风格只是针对想要在其他屏幕上重用它的情况。