检测用户或程序是否更改了EditText的内容?

70
我需要一种方式来检测EditText是否被用户输入更改或者应用程序以编程方式更改文本。有任何标准方法吗?我想我总是可以像在setText()之前取消TextWatcher,之后再设置回来一样做一些hackish的事情,但肯定有更好的方法... 对吧?
我尝试在TextWatcher中检查EditText是否聚焦,但因为在滚动时EditText会“半随机”聚焦,所以那没什么用...
背景:我有一个ListView,在每个列表项中都有EditTexts。我解决了存储EditTexts的值以便在用户滚动时重复使用的基本问题。
我还有一个TextWatcher,在用户编辑任何EditTexts的内容时,它会将所有EditTexts的值相加并显示总和。
问题是,当我滚动列表并且我的自定义适配器正在重新绑定视图上存储的值到EditTexts时,这也会触发TextWatchers afterTextChanged()方法,从而导致滚动出现延迟,因为求和函数被触发了。

你可以重写OnKeyDown()方法,在其中触发一个布尔值,以便知道用户正在输入文本,并在TextWatcher中使用它。 - JRowan
1
@JRowan 是的,我考虑放弃 TextWatcher,转而依赖于 onKeyDown(),但根据文档说明:“软键盘中的按键通常不会触发此监听器,尽管某些情况下可能会选择这样做。不要依赖于此来捕获软件按键。” http://developer.android.com/reference/android/widget/TextView.html#onKeyDown(int, android.view.KeyEvent) - Magnus
@BadCash,你有多少个TextWatcher? - pskink
在你的代码中调整类变量时,设置一个标志位,然后在你的文本监视器中检查该标志位,最后重置它,这个方案怎么样? - Matt Clark
@pskink 在ListView中每个项目都是在newItem()方法中实例化的。 - Magnus
当视图被绑定时,我成功地清除了焦点。然后只有在EditText实际上具有焦点时才对文本编辑做出反应。 - Diederik
6个回答

68

这个问题很久之前就被解决了,但是对于任何寻找答案的人,这是我所做的:

在我即将通过编程方式更改EditText之前,我最终将EditText的标签设置为某个任意值,并更改该值,然后将标签重置为null。 然后,在我的TextWatcher.afterTextChanged()方法中,我检查标签是否为空来确定是用户还是程序更改了该值。 非常完美!

像这样:

edit.setTag( "arbitrary value" );
edit.setText( "My Text Value" );
edit.setTag(null);

然后

public void afterTextChanged(Editable s) {
    if( view.getTag() == null )             
        // Value changed by user
    else
        // Value changed by program
}

对我很有用!感谢您提供简单易行的解决方案。 - MBH
这个解决方案非常棒。它解决了我们一年多以来一直遇到的问题。唯一需要注意的是,在协程块内不应检查editText的标签 - 请确保在块外设置一个变量,然后在块内进行检查。 - beyondtheteal
view.getTag() 出错了,怎么回事? - Learn2Code
view.getTag() 在那里出错了? - undefined

11

这个被接受的答案完全有效,但我有另一种方法;

@Override
public void onTextChanged(CharSequence charSequence, 
                         int start, int before, int count) {
    boolean userChange = Math.abs(count - before) == 1; 
    if (userChange) { 

    }
}

它的工作原理是检查更改是否为单个字符。 这不是一种万无一失的解决方案,因为复制粘贴操作可能会被忽略,并且单个字符的非用户更改也将被忽略。 根据您的使用情况,这可能是一个可行的解决方案。


1
最佳解决方案...易于理解 - Pankaj Kumar
1
用户在EditText中粘贴内容时会发生什么?;) - reVerse
1
当用户点击自动建议的单词时会发生什么? - Aloha
2
答案错误。如果使用任何剪切/粘贴或高亮编辑,将会产生错误的结果。完全错误。 - Mitch
1
如果您以程序方式更改一个字符会怎样?这不是一个好的解决方案。 - mithunc
显示剩余2条评论

6

根据您的使用情况(例如,当用户在另一个字段中输入时自动填充此字段),您还可以检查视图是否具有焦点,例如:

textView.doAfterTextChanged {
    val isValueChangedByUser = textView.hasFocus()
    // ...
}

2
有一件事对我有帮助,那就是拥有一个名为 canListenInput 的布尔类型字段。在 watcher 中使用它即可。
    email.addTextChangedListener(new TextWatcher() {
        @Override
        public void afterTextChanged(Editable s) {
            if (canListenInput) {
                emailChanged = true;
            }
        }
    });

在以编程方式更改文本之前,请清除它。将其设置在onAttachedToWindow之内,在(状态)恢复后:

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    canListenInput = true;
}

0

我已经创建了一些扩展方法来解决这种情况。

inline fun TextView.runTaggingCode(block: () -> Unit) {
    this.setTag(R.string.tag_text_id, "set_from_code")
    block()
    this.setTag(R.string.tag_text_id, null)
}

fun TextView.isTaggedForCode() = this.getTag(R.string.tag_text_id) != null

我已经定义了R.string.tag_text_id如下:

<string name="tag_text_id" translatable="false">dummy</string>

现在如果我要使用这些方法,我只需要将我的代码更改如下:

override fun beforeTextChanged(
    s: CharSequence, start: Int, count: Int,
    after: Int,
) {
    if (textView.isTaggedForCode()) {
        return
    }
    textView.runTaggingCode { 
        // your logic here
    }
}

但是如果您不想更改相同的文本视图文本,在它自己的TextWatcher中,您也可以查看答案


-1

您可以通过添加以下内容来实现:

private String current = "";
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(!s.toString().equals(current)){
   [your_edittext].removeTextChangedListener(this);

   //Format your string here...

   current = formatted;
   [your_edittext].setText(formatted);
   [your_edittext].setSelection(formatted.length());

   [your_edittext].addTextChangedListener(this);
}

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