TextWatcher事件被多次触发

时间:2013-07-08 20:27:22

标签: android android-layout textwatcher

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

出于某种原因,在一次文本更改时调用TextWatcher事件是不稳定的。有时它们会被触发一次(就像它们应该的那样),有时是两次,有时是3次。不知道为什么,整件事情非常直截了当。有时候,afterTextChanged()上的Editable参数会在toString()和length()中返回空值。

代码如下:

    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视图进行任何更改。

我在Events of TextWatcher are being called twice中看到了问题,但即时通讯触发的事件多于(或少于)两次。

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

编辑:我删除了afterTextChanged()的内容,导致即使没有我的代码也会出现此问题。是什么让我相信这是一个错误。当在常规字符(事件处理程序被触发两次)之后输入'space'字符时或者在删除常规字符之后的'space'字符(退格。事件处理程序被触发3次)时,会发生错误。帮助仍然会受到赞赏。

7 个答案:

答案 0 :(得分:48)

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

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

android:inputType="textNoSuggestions"

不要问我为什么,但它有效,afterTextChanged现在只调用一次。

答案 1 :(得分:7)

根据TextWatcher的开发人员页面,如果对Editable中的TextWatcher进行了更改,则会触发对所有与TextWatchers相关联的调用Editable。现在,显然您的代码不会触发此行为。

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

首先,经典辩护:没有理由不发生这种情况,严格来说,应该编写应用代码以使其具有弹性。

其次,我无法证明这一点,但我可以想象,处理EditText中显示文本布局的代码使用TextWatcher来处理更新文本的显示屏幕。此代码可能会将控制代码(未显示)插入Editable以确保良好的换行符等。它甚至可以循环几次以使其正确,并且您可能只有在完成所有操作后才能进行第一次调用...

修改

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

答案 2 :(得分:3)

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
    }

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

我尝试了在这个问题上回答的所有解决方案,但没有一个能为我工作。但经过一些搜索,我发现了this帖子。使用RxJava进行去抖动对我来说效果很好。这是我的最终解决方案:

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

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

实施您的主题:

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

在TextWatcher中使用您的主题:

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:

my_edit_text.addTextChangedListener(myTextWatcher);

答案 5 :(得分:0)

对我来说,以下代码有效。请注意,在 afterTextChanged 方法中的 if 条件循环后,布尔值已更改

        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
            }


        });

答案 6 :(得分:0)

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

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

根据上述Nico's的一些启示,这对我来说使输入文本更改仅触发一次是有效的。

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

1)通过样式

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

,然后在xml中用作:

<EditText
   android:id="@+id/my_edit_text"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   style="@style/My.Text.Style"
   />

OR

2)直接进入xml

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

这两种方法都应该很好,这种样式仅适合于希望在其他屏幕上重用它的情况。