【问题标题】:Android TextWatcher FiringAndroid TextWatcher 触发
【发布时间】:2012-03-31 03:57:45
【问题描述】:

我有一个 Android 编程问题。使用下面的代码,我想验证字符串匹配。它验证得很好,但 LogCat 显示 TextWatcher 方法每次击键都会触发两次,我不知道为什么。我希望每次击键只触发一次。

你知道它为什么这样做吗?

我认为这可能是因为我更改了文本的颜色,但在将其注释掉后并没有什么不同。

LogCat 输出

03-31 03:37:25.269: I/BeforeText(676): Hit 
03-31 03:37:25.269: I/OnText(676): Hit
03-31 03:37:25.269: I/AfterText(676): Hit
03-31 03:37:25.274: I/InvalidText(676): Incorrect Text.
03-31 03:37:25.274: I/Text Value(676): a
03-31 03:37:25.404: I/BeforeText(676): Hit
03-31 03:37:25.404: I/OnText(676): Hit
03-31 03:37:25.404: I/AfterText(676): Hit
03-31 03:37:25.404: I/InvalidText(676): Incorrect Text.
03-31 03:37:25.404: I/Text Value(676): a

活动代码

public void onCreate(Bundle savedInstanceState) {

     //...omitted

    //Create Answer Field
    textField = (EditText)this.findViewById(R.id.textField);

    //Add validation to TextField
    textField.addTextChangedListener(new TextWatcher(){
        public void afterTextChanged(Editable s){

            Log.i("AfterText","Hit");

            if(textField.getText().toString().trim().equalsIgnoreCase("hello")){
                Log.i("ValidText", "Text matched.");

                answerField.setTextColor(Color.GREEN);

            }
            else{
                Log.i("InvalidText", "Incorrect text.");
                Log.i("Text Value", textField.getText().toString());

                textField.setTextColor(Color.RED);

            }
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after){
            //Do nothing
            Log.i("BeforeText", "Hit");
        }

        public void onTextChanged(CharSequence s, int start, int before, int count){
            //Do nothing
            Log.i("OnText","Hit");

        }
    });
}

【问题讨论】:

  • 通过比较s.toString().trim().equalsIgnoreCase("hello")等字符序列来检查。
  • 我有同样的问题,我想我找到了它的来源,但是......我仍然没有解决方案。就我而言,似乎当您点击空格时会创建一个新的跨度,这似乎会触发两个事件。如果您尝试将编辑文本从“a b”更改为“a”,您将看到下一个错误日志:E/SpannableStringBuilder(24004): SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length

标签: android textwatcher


【解决方案1】:

因为您的问题是针对 TextWatcher 方法每次击键触发两次。 您已在 EditText 上使用 TextWather 进行监视以进行 Validate String 并设置 Color 。

您可以在此处参考开发人员站点中的 TextWatcher 文档。 http://developer.android.com/reference/android/text/TextWatcher.html.

当您按下 keystore 时,它​​将以 TextWatcher 方法 onTextChanged 调用的方式对 EditText 文本进行更改,当您按下 EditText 方法 beforeTextChanged 的任意键时,它将在我们开始编辑 EditText 时调用。

还有一件事是,当您在 EditText 中输入一个字符时,它会调用 Textwather 的所有这三个方法。只是 Call 的顺序不同。并且还引用了这个 SO Question Android TextWatcher.afterTextChanged vs TextWatcher.onTextChanged

所以在EditText中调用两次Text Change是没有错的。

希望你能理解。

【讨论】:

  • 嗨,Herry,我有同样的问题,不,我不明白为什么该方法被调用了两次。在我的情况下,仅当我点击空格或 & 或类似的奇怪字符时才会发生。例如,如果文本是“a”并且我按下空格,则调用该方法两次,一次告诉我新字符串是“a”,第二次告诉我文本是“a”。我的问题是第一种方法触发了一个 onchange 逻辑,使我进入了一个无限循环。你能解释一下为什么这些方法被调用了两次吗?
  • 当我说“方法”时,我指的是 afterTextChanged 方法。
  • @CarlosVerdes 当您的 Edittext 中有 'a' 并且现在您在 EditText 中添加空格时,它将按此顺序调用方法,首先是 beforeTextChange,onTextChanged,最后是 afterTextChanged。
  • @CarlosVerdes 它不会调用 afterTextChanged 方法两次只在 Edittext 中添加空格。
  • @Herry... 相信我,它确实调用了两次,一个说新的 val 是“a”,第二个说新的 val 是“a”。我认为问题在于滑动检测空格以向您显示建议。如果你有刷卡只是尝试做一个简单的测试插入和删除空格,你会看到奇怪的行为(我在 Nexus 5 中做到了)。还要检查我的回答网址,它谈到了这一点。
【解决方案2】:

我认为这不会对您有所帮助,但就我而言,它似乎是滑动键盘。

关键是,当滑动尝试提出建议时,onchange 方法似乎被调用了两次(一个来自 EditText,另一个来自滑动键盘)。

我不知道这在您的代码中是否可行,但只需在您的 EditText 上尝试一下,看看会发生什么:

android:inputType="textNoSuggestions"

您可以查看下一个讨论此问题的网站:

http://support.swiftkey.net/forums/116693-2-bug-reports/suggestions/2994580-span-exclusive-exclusive-spans-cannot-have-a-zero-

如果我找到解决方案,我会编辑这个答案。


编辑


我的结论是,我无法避免中间事件,也无法区分来自用户的真实更改事件或滑动更改事件。

根据您的实际问题,您应该尝试不同的解决方法。一种解决方法可能是将更新的信息保存在 ConcurrentHashMap 中,并在收到事件后 100 毫秒安排任务。同时你应该设置一个“lastModified”日期。

在计划任务中,您应该询问“距离上次更新已经过去 100 毫秒?”,如果答案是否定的 --> 什么都不做(这意味着另一个事件已经到达,因此将使用最后一个值执行另一个更新任务)。

所有都带有 ReentrantLock 以达到原子性。

类似这样的:

   private final ReentrantLock lock = new ReentrantLock();
    private final ScheduledExecutorService scheduleExecutor = new ScheduledThreadPoolExecutor(1);
    private Date lastModification = null;
    private static final long UPDATE_WINDOW_IN_MILLISECONDS = 100;

protected class UpdateBusinessLogicTask implements Runnable {
    private final String newVal;

    public UpdateBusinessLogicTask(String newVal) {
        super();
        this.newVal = newVal;
    }

    @Override
    public void run() {

        //check current date
        Date now = new Date();
        //get lock to achieve atomicity 
        lock.lock();

        // calculate elapsed time
        long elapsedTime = lastModification.getTime() - now.getTime();

        // free the lock --> you could free the lock after business logic, depends on your scenario
        lock.unlock();

        //if the time is less than the updating window (100 milliseconds)
        if (elapsedTime < (UPDATE_WINDOW_IN_MILLISECONDS - 1)) {
            // DO NOTHING!!
            Log.d("TEST", "Dismiss update "+newVal);
        } else {
            Log.d("TEST", "Updating business with newVal: "+newVal);
            // TODO implement business logic
        }

    }
};

protected void onCreate(Bundle savedInstanceState) {

    EditText view = findViewById(R.id.nameEditText);

    TextWatcher tw = new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            Log.d("TEST", "New text event received, new val: " + s.toString());

            lock.lock();
            lastModification = new Date();

            scheduleExecutor.schedule(new UpdateBusinessLogicTask(s.toString()), UPDATE_WINDOW_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
            lock.unlock();
        }

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

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

}

【讨论】:

    【解决方案3】:

    也许你有两个 textWatcher,textField.addTextChangedListener 方法被调用了两次。

    【讨论】:

      【解决方案4】:

      您可以使用布尔值。当用户更改文本时设置它,当您的自定义代码触发时重置它。该事件仍会触发两次,但您的代码仅被调用一次。 最好的问候。

      【讨论】:

        【解决方案5】:

        使用这个,它可能会帮助你:

        myInput.addTextChangedListener(new TextWatcher() {
            boolean mToggle = false;
        
            public void onTextChanged(CharSequence cs, int s, int b, int c) {}
        
            public void afterTextChanged(Editable editable) {
                if (mToggle)  
                    Toast.makeText(getBaseContext(), "HIT KEY", Toast.LENGTH_LONG).show();
                mToggle = !mToggle;
            }
        });
        

        【讨论】:

        • 这不起作用,因为有时会启动一个事件,有时会启动 2 个,有时会启动 3 个...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-23
        • 2013-07-06
        • 1970-01-01
        • 2015-02-28
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        相关资源
        最近更新 更多