【问题标题】:TextWatcher events are being fired multiple timesTextWatcher 事件被多次触发
【发布时间】:2013-07-06 07:17:04
【问题描述】:

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() 的内容,因为即使没有我的代码也会发生此问题。是什么让我相信这是一个错误。当在常规字符后立即输入“空格”字符(触发事件处理程序两次)或删除常规字符后的“空格”字符(退格键。事件处理程序被触发 3 次)时,会发生该错误。我们将不胜感激。

【问题讨论】:

  • 你想用那个适配器做什么?尝试检查adapter.getCount() > 0,然后清除它。因为如果你得到适配器并清除它,适配器仍然不会为空。
  • @deadfish 适配器并不是真正的问题。它只是一个基于在 TextWatcher 中输入的文本的列表。正如我所说,问题在于调用 TextWatcher 侦听器上的事件的次数。这对我来说完全是随机的。
  • 是的,但是每次您更改与 textwatcher 连接的视图时都会调用 textwatcher(更改文本状态),您关注我吗?
  • 不,抱歉。适配器未连接到 TextWatcher。它连接到另一个 ListView。更改/清除它应该对此 TextWatcher 没有影响。
  • 您注册了多少次同一个观察者?因为当您执行 addTextChangedListener() 时,它会将观察者的每个实例保存在列表中,并且当需要通知文本观察者时,它会遍历观察者列表。

标签: android android-layout textwatcher


【解决方案1】:

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

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

android:inputType="textNoSuggestions"

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

【讨论】:

  • 发生这种情况是因为在用户键入空格的情况下,某些设备会将前一个单词大写(如果它是第一个单词并且 inputType="name")。因此, afterTextChanged 被调用了两次。一次用于空格,一次用于大写。
  • 这适用于尊重此输入类型的键盘。对于需要建议栏的外语(即日语),仍然会调用该方法两次。
  • 遇到同样的问题,直到现在 API 27 Asus
  • 请注意,android:inputType="number" 也可以在我的情况下使用,因为我需要“钱”输入
【解决方案2】:
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
    }

【讨论】:

  • 欢迎 velraj - 请添加一些文字来解释您的代码逻辑 - 这将有助于其他人理解原始海报。请阅读如何回答 - stackoverflow.com/help/how-to-answer。例如,解释它与 usman 的答案有何不同。
【解决方案3】:

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

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

首先,经典的防御:没有理由不发生这种情况,严格来说,应用程序代码应该被编写成能够适应它。

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

编辑

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

【讨论】:

  • 看起来是自动更正的;关闭它可以修复奇怪的回调。
【解决方案4】:

我尝试了关于这个问题的所有解决方案,但没有一个对我有用。但经过一番搜索,我找到了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);

【讨论】:

  • 解决方案是否处理退格?
  • 是的,字符串中的任何更改都会调用 TextWatcher。
  • debounce,“这个 bug 是从哪里来的?”这样一个漂亮的名字
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

    对我来说,下面的代码有效。请注意在 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
                }
    
    
            });
    

    【讨论】:

      【解决方案7】:

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

      这不会发生在物理设备中。

      根据上述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"
         />
      

      2) 直接进入xml

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

      这两种方式都应该没问题,这种风格只是为了在其他屏幕上重复使用它的情况。

      【讨论】:

        【解决方案8】:

        假设每个字符的打字速度为 300 毫秒。

            private var textDispatcherHandler = Handler(Looper.getMainLooper())
            private var textDispatcherRunnable:Runnable? = null
        
            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
                if(p0!=null){
                    dispatchTextToFilter(p0.toString())
                }
            }
            
            private fun dispatchTextToFilter(string: String){
                if(textDispatcherRunnable!=null){
                    textDispatcherHandler.removeCallbacks(textDispatcherRunnable!!)
                }
            
                if(textDispatcherRunnable==null){
                    textDispatcherRunnable = Runnable {
                        //doSomething here
                    }
                }
                textDispatcherHandler.postDelayed(textDispatcherRunnable!!,300)
            }
        

        【讨论】:

          猜你喜欢
          • 2018-09-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多