Detect if content of EditText was changed by user

2019-03-08 22:39发布

问题:

I would need a way to detect if the EditText has been changed by the user typing something or by the app changing the text programmatically. Any standard way of doing this? I guess I could always do something hackish like unsetting the TextWatcher before setText() and setting it back again afterwards, but there's got to be a better way of doing this... right?

I tried checking if the EditText is focused in the TextWatcher, but that was of little help since the EditTexts gets focused "semi-randomly" anyway when scrolling...

 

Background

I have a ListView with EditTexts in every listitem. I've sorted out the basic problem of storing the values for the EditTexts for reuse when the user scrolls.

I also have a TextWatcher that sums up the values in all EditTexts and displays the sum when the user edits the content of any of the EditTexts.

The problem is that when I'm scrolling the list and my custom adapter is reentering the stored values in the EditTexts on bindView(), that also triggers the TextWatchers afterTextChanged() method, causing the scrolling to lag because the summing-up-function is triggered.

回答1:

This sorted itself out a long time ago, but for anyone who finds their way here looking for an answer, here's what I did:

I ended up setting the Tag of the EditText to some arbitrary value right before I'm about to change it programmatically, and changing the value, and then resetting the Tag to null. Then in my TextWatcher.afterTextChanged() method I check if the Tag is null or not to determine if it was the user or the program that changed the value. Works like a charm!

Something like this:

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

and then

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


回答2:

The accepted answer is perfectly valid, but I have another approach;

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

    }
}

It works by checking if the change was a single character. This is not a fool-proof solution as copy-paste operations might be missed, and non-user changes of a single character will also be missed. Depending on your use case, this might be a viable solution.



回答3:

One thing that helped to me is having boolean canListenInput field. Use it inside of watcher.

    email.addTextChangedListener(new TextWatcher() {
        @Override
        public void afterTextChanged(Editable s) {
            if (canListenInput) {
                emailChanged = true;
            }
        }
    });

Clear it before changing text programmatically. Set it inside of onAttachedToWindow, (after state) restoration:

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


回答4:

You can do this by adding:

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