Detect if content of EditText was changed by user or programmatically?

AndroidListviewAndroid EdittextTextwatcher

Android Problem Overview


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.

Android Solutions


Solution 1 - Android

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
}

Solution 2 - Android

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.

Solution 3 - Android

Depending on your use case (e.g. you are auto-populating this field when the user types into another field), you can also check if the view has focus, e.g.:

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

Solution 4 - Android

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

Solution 5 - Android

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMagnusView Question on Stackoverflow
Solution 1 - AndroidMagnusView Answer on Stackoverflow
Solution 2 - AndroidPetrusView Answer on Stackoverflow
Solution 3 - AndroidVasiliy KulakovView Answer on Stackoverflow
Solution 4 - AndroidkonmikView Answer on Stackoverflow
Solution 5 - AndroidMorteza RastgooView Answer on Stackoverflow