131

I want to catch an event when the user finishes editing EditText.

How can it be done?

Epicality
  • 750
  • 1
  • 8
  • 23
eddyuk
  • 3,760
  • 4
  • 30
  • 53
  • 1
    @PadmaKumar The most natural way would be if the focus gets lost on the EditText - then the user is most definitely done, however from experience it seems that not all android widgets are properly grabbing focus (I had those issues with Spinners and Buttons) - so other widgets might not lose focus... – AgentKnopf Apr 30 '12 at 21:16
  • 3
    why such a big platform let us deal with this kind of simple functions? google will not lose anything if they add such main features to their support libraries at least – MBH Mar 09 '17 at 12:46
  • If you know the exact length of the string user gonna enter then, get the text in afterTextChanged. Ex: `override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }` – Shailendra Madda Oct 13 '18 at 14:27
  • 2
    I am scratching my head in wonder at why this is so difficult in Android that we (myself included) need to search for and start an online discussion and many different solutions with 10+ lines of code..? – nsandersen Jun 01 '19 at 10:29

15 Answers15

192

Better way, you can also use EditText onFocusChange listener to check whether user has done editing: (Need not rely on user pressing the Done or Enter button on Soft keyboard)

 ((EditText)findViewById(R.id.youredittext)).setOnFocusChangeListener(new OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) { {
         // Validate youredittext
      }
    }
 });

Note : For more than one EditText, you can also let your class implement View.OnFocusChangeListener then set the listeners to each of you EditText and validate them as below

((EditText)findViewById(R.id.edittext1)).setOnFocusChangeListener(this);
((EditText)findViewById(R.id.edittext2)).setOnFocusChangeListener(this);

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) {
        switch (view.getId()) {
           case R.id.edittext1:
                 // Validate EditText1
                 break;
           case R.id.edittext2:
                 // Validate EditText2
                 break;
        }
      }
    }
Vinayak Bevinakatti
  • 38,839
  • 25
  • 103
  • 135
  • 61
    This doesn't work if there is only one focusable `EditText`. It will never lose focus. – Bart Friederichs Oct 27 '15 at 16:03
  • What is the best way if we only have a single `EditText`? Do I have to use `onEditorAction`? – Tux Nov 24 '15 at 08:36
  • 3
    If there's only one `EditText`, you validate on whatever the user does to leave the screen. – Christine May 11 '16 at 14:55
  • I tried this but it did not work. Every time I pressed enter it would give me a message similar to this: `W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=605.52246, y[0]=969.4336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=238238, downTime=235422, deviceId=0, source=0x1002 }` Even though I typed something in. This is a edittext that only accepts numbers. – ahitt6345 Jul 13 '16 at 01:14
  • I have created a question concerning the issue here: http://stackoverflow.com/questions/38341339/cancelling-event-due-to-no-window-focus-motionevent – ahitt6345 Jul 13 '16 at 19:08
  • It won't work if there are more than 2 edit texts either – Hitesh Sahu Feb 10 '17 at 10:18
  • I'm using this solution w/ 3 edittexts so I don't know what the problem is Hitesh Sahu. – JDOaktown Apr 18 '17 at 20:14
  • 1
    There is a sidepoint of using OnFocusChange listener: it will not get called if EditText has focus and device gets rotated! I solved it by putting someOtherView.requestFocus() in Activities onPause(). (before super.onPause()) That way it's safe that EditText looses focus and listener gets called. – allofmex Oct 06 '17 at 11:48
  • You can make your Edittext loose focus using this: https://stackoverflow.com/a/46127051/5036416 – Marion Piloo Nov 24 '20 at 15:33
121

When the user has finished editing, s/he will press Done or Enter

((EditText)findViewById(R.id.youredittext)).setOnEditorActionListener(
    new EditText.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH ||
                    actionId == EditorInfo.IME_ACTION_DONE ||
                    event != null &&
                    event.getAction() == KeyEvent.ACTION_DOWN &&
                    event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
                if (event == null || !event.isShiftPressed()) {
                   // the user is done typing. 

                   return true; // consume.
                }                
            }
            return false; // pass on to other listeners. 
        }
    }
);
Cheok Yan Cheng
  • 49,649
  • 117
  • 410
  • 768
Reno
  • 32,851
  • 11
  • 85
  • 99
  • 55
    Can u be sure about it? I have the habit of tapping into/onto the next editable field - I pretty much never press Enter/Done - and what I've seen from our customers, neither do they... I am talking about a list of Edittexts/Comboboxes etc. If you only give the user one Input field and force him to press a button before he can advance to other fields then that's obviously a different story... – AgentKnopf Apr 30 '12 at 21:15
  • 5
    Remember, you also have to have android:singleLine="true" set for your edit text. Thanks to http://stackoverflow.com/questions/15901863/oneditoractionlistener-not-working – steven smith Mar 15 '15 at 17:58
  • 1
    Good solution but can crash with null pointer... 'event' can be null on Done action so event.isShiftPressed() crashes. If you use this add null pointer checks. – Georgie Jun 16 '17 at 23:27
  • Edit added to check for null "Event". – Word Rearranger Jan 26 '18 at 14:57
  • I have added EditorInfo.IME_ACTION_NEXT will this suffice? – Michał Ziobro Aug 09 '18 at 10:55
  • 5
    what if you hit BACK button that closes the keyboard? – user347187 Sep 04 '19 at 20:35
64

I personally prefer automatic submit after end of typing. Here's how you can detect this event.

Declarations and initialization:

private Timer timer = new Timer();
private final long DELAY = 1000; // in ms

Listener in e.g. onCreate()

EditText editTextStop = (EditText) findViewById(R.id.editTextStopId);
    editTextStop.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
        }
        @Override
        public void onTextChanged(final CharSequence s, int start, int before,
                int count) {
            if(timer != null)
                timer.cancel();
        }
        @Override
        public void afterTextChanged(final Editable s) {
            //avoid triggering event when text is too short
            if (s.length() >= 3) {              

                timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // TODO: do what you need here (refresh list)
                        // you will probably need to use
                        // runOnUiThread(Runnable action) for some specific
                        // actions
                        serviceConnector.getStopPoints(s.toString());
                    }

                }, DELAY);
            }
        }
    });

So, when text is changed the timer is starting to wait for any next changes to happen. When they occure timer is cancelled and then started once again.

ZimaXXX
  • 1,135
  • 10
  • 11
22

You can do it using setOnKeyListener or using a textWatcher like:

Set text watcher editText.addTextChangedListener(textWatcher);

then call

private TextWatcher textWatcher = new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            //after text changed
        }

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

        @Override
        public void afterTextChanged(Editable s) {

        }
    };
onof
  • 16,589
  • 7
  • 46
  • 82
ShineDown
  • 1,833
  • 3
  • 21
  • 29
  • 29
    I am afraid this is not the (complete) solution - the textWatcher fires after each edit - if you type hello it's gonna fire for h, e, l, l and o - it doesn't really know when the user is "done". Would be great if the TextWatcher would actually know when the widget lost focus and the user moved on... – AgentKnopf Apr 30 '12 at 21:13
6

I solved this problem this way. I used kotlin.

        var timer = Timer()
        var DELAY:Long = 2000

        editText.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable?) {
                Log.e("TAG","timer start")
                timer = Timer()
                timer.schedule(object : TimerTask() {
                    override fun run() {
                        //do something
                    }
                }, DELAY)
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                Log.e("TAG","timer cancel ")
                timer.cancel() //Terminates this timer,discarding any currently scheduled tasks.
                timer.purge() //Removes all cancelled tasks from this timer's task queue.
            }
        })
Fahed Hermoza
  • 261
  • 2
  • 10
5

both @Reno and @Vinayak B answers together if you want to hide the keyboard after the action

textView.setOnEditorActionListener(new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(textView.getWindowToken(), 0);
            return true;
        }
        return false;
    }
});

textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
             // your action here
        }
    }
});
Tyler Davis
  • 2,361
  • 2
  • 20
  • 18
4

Although many answers do point in the right direction I think none of them answers what the author of the question was thinking about. Or at least I understood the question differently because I was looking for answer to similar problem. The problem is "How to know when the user stops typing without him pressing a button" and trigger some action (for example auto-complete). If you want to do this start the Timer in onTextChanged with a delay that you would consider user stopped typing (for example 500-700ms), for each new letter when you start the timer cancel the earlier one (or at least use some sort of flag that when they tick they don't do anything). Here is similar code to what I have used:

new Timer().schedule(new TimerTask() {
  @Override
  public void run() {
     if (!running) {                            
        new DoPost().execute(s.toString());
  });
 }
}, 700);

Note that I modify running boolean flag inside my async task (Task gets the json from the server for auto-complete).

Also keep in mind that this creates many timer tasks (I think they are scheduled on the same Thread thou but would have to check this), so there are probably many places to improve but this approach also works and the bottom line is that you should use a Timer since there is no "User stopped typing event"

Igor Čordaš
  • 5,018
  • 2
  • 39
  • 53
  • Can you please give more information and sample code on this? – John Ernest Guadalupe Nov 02 '15 at 13:41
  • The gist of code is in the answer, for more detailed example please see answer by ZimaXXX on this same page which uses same approach. I don't know what else info I could add, if you can specify what is not clear I will try to add details. – Igor Čordaš Nov 02 '15 at 13:46
2

Okay this will work 100% for sure.

First you will need to setup listener if keyboard is show or hide. If keyboard is showing then probably user is typing, otherwise done typing.

final View activityRootView = findViewById(android.R.id.content);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    activityRootView.getWindowVisibleDisplayFrame(r);

                    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
                    if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...

                        isTyping = true;
                    } else {
//to make sure this will call only once when keyboard is hide.
                        if(isTyping){
                            isTyping = false;
                        }
                    }
            }
        });
Krit
  • 570
  • 1
  • 5
  • 18
  • 1
    what about multiple text edits? And the `heightDiff > 100` stuff is scary imho. – Bondax Dec 02 '14 at 12:15
  • No problem, it works fine. Have a look at this http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android – Krit Dec 02 '14 at 12:51
2

A different approach ... here is an example: If the user has a delay of 600-1000ms when is typing you may consider he's stopped.

 myEditText.addTextChangedListener(new TextWatcher() {
             
            private String s;
            private long after;
   private Thread t;
            private Runnable runnable_EditTextWatcher = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        if ((System.currentTimeMillis() - after) > 600)
                        {
                            Log.d("Debug_EditTEXT_watcher", "(System.currentTimeMillis()-after)>600 ->  " + (System.currentTimeMillis() - after) + " > " + s);
                            // Do your stuff
                            t = null;
                            break;
                        }
                    }
                }
            };
            
            @Override
            public void onTextChanged(CharSequence ss, int start, int before, int count) {
                s = ss.toString();
            }
            
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            
            @Override
            public void afterTextChanged(Editable ss) {
                after = System.currentTimeMillis();
                if (t == null)
                {
                    t = new Thread(runnable_EditTextWatcher);
                      t.start();
                }
            }
        });
Cristi Maris
  • 1,081
  • 19
  • 18
1

I have done something like this abstract class that can be used in place of TextView.OnEditorActionListener type.

abstract class OnTextEndEditingListener : TextView.OnEditorActionListener {

    override fun onEditorAction(textView: TextView?, actionId: Int, event: KeyEvent?): Boolean {

        if(actionId == EditorInfo.IME_ACTION_SEARCH ||
                actionId == EditorInfo.IME_ACTION_DONE ||
                actionId == EditorInfo.IME_ACTION_NEXT ||
                event != null &&
                event.action == KeyEvent.ACTION_DOWN &&
                event.keyCode == KeyEvent.KEYCODE_ENTER) {

            if(event == null || !event.isShiftPressed) {
                // the user is done typing.
                return onTextEndEditing(textView, actionId, event)
            }
        }
        return false // pass on to other listeners
    }

    abstract fun onTextEndEditing(textView: TextView?, actionId: Int, event: KeyEvent?) : Boolean
}
Michał Ziobro
  • 7,390
  • 6
  • 50
  • 99
1

Simple to trigger finish typing in EditText

worked for me , if you using java convert it

In Kotlin

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
0

I had the same issue and did not want to rely on the user pressing Done or Enter.

My first attempt was to use the onFocusChange listener, but it occurred that my EditText got the focus by default. When user pressed some other view, the onFocusChange was triggered without the user ever having assigned it the focus.

Next solution did it for me, where the onFocusChange is attached if the user touched the EditText:

final myEditText = new EditText(myContext); //make final to refer in onTouch
myEditText.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            myEditText.setOnFocusChangeListener(new OnFocusChangeListener() {

                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if(!hasFocus){
                        // user is done editing
                    }
                }
            }
        }
}

In my case, when the user was done editing the screen was rerendered, thereby renewing the myEditText object. If the same object is kept, you should probably remove the onFocusChange listener in onFocusChange to prevent the onFocusChange issue described at the start of this post.

Jos
  • 823
  • 1
  • 10
  • 23
  • You are setting a focus change listener every time a touch event happens, which will be several times a second. Do not do this. – jsonfry Jan 07 '15 at 17:23
0

I had the same problem when trying to implement 'now typing' on chat app. try to extend EditText as follows:

public class TypingEditText extends EditText implements TextWatcher {

private static final int TypingInterval = 2000;


public interface OnTypingChanged {
    public void onTyping(EditText view, boolean isTyping);
}
private OnTypingChanged t;
private Handler handler;
{
    handler = new Handler();
}
public TypingEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context) {
    super(context);
    this.addTextChangedListener(this);
}

public void setOnTypingChanged(OnTypingChanged t) {
    this.t = t;
}

@Override
public void afterTextChanged(Editable s) {
    if(t != null){
        t.onTyping(this, true);
        handler.removeCallbacks(notifier);
        handler.postDelayed(notifier, TypingInterval);
    }

}

private Runnable notifier = new Runnable() {

    @Override
    public void run() {
        if(t != null)
            t.onTyping(TypingEditText.this, false);
    }
};

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


@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { }

}

Nadav
  • 1
0

I ended her with the same problem and I could not use the the solution with onEditorAction or onFocusChange and did not want to try the timer. A timer is too dangerous for may taste, because of all the threads and too unpredictable, as you do not know when you code is executed.

The onEditorAction do not catch when the user leave without using a button and if you use it please notice that KeyEvent can be null. The focus is unreliable at both ends the user can get focus and leave without enter any text or selecting the field and the user do not need to leave the last EditText field.

My solution use onFocusChange and a flag set when the user starts editing text and a function to get the text from the last focused view, which I call when need.

I just clear the focus on all my text fields to tricker the leave text view code, The clearFocus code is only executed if the field has focus. I call the function in onSaveInstanceState so I do not have to save the flag (mEditing) as a state of the EditText view and when important buttons is clicked and when the activity is closed.

Be careful with TexWatcher as it is call often I use the condition on focus to not react when the onRestoreInstanceState code entering text. I

final EditText mEditTextView = (EditText) getView();

    mEditTextView.addTextChangedListener(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) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            if (!mEditing && mEditTextView.hasFocus()) {
                mEditing = true;
            }
        }
    });
    mEditTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {

        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus && mEditing) {
                mEditing = false;
                ///Do the thing
            }
        }
    });
protected void saveLastOpenField(){
    for (EditText view:getFields()){
            view.clearFocus();
    }
}
Rezder
  • 11
  • 1
0

Use this class

DelayedTextWatcher.java

import android.os.Handler;
import android.text.Editable;
import android.text.TextWatcher;
public class DelayedTextWatcher implements TextWatcher {

    private String text;
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            listener.onTimeout(text);
        }
    };

    public static interface DelayedTextWatcherListener {
        public void onTimeout(CharSequence text);
    }

    private long DELAY = 200;
    private Handler handler = new Handler();
    private DelayedTextWatcherListener listener;

    public DelayedTextWatcher(DelayedTextWatcherListener l) {
        listener = l;
    }

    @Override
    public void afterTextChanged(Editable s) {
        text = s.toString();
        handler.postDelayed(runnable, DELAY);
    }

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

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        handler.removeCallbacks(runnable);
    }

}
Omkar
  • 338
  • 3
  • 10