236

I am having an EditText where I am setting the following property so that I can display the done button on the keyboard when user click on the EditText.

editText.setImeOptions(EditorInfo.IME_ACTION_DONE);

When user clicks the done button on the screen keyboard (finished typing) I want to change a RadioButton state.

How can I track done button when it is hit from screen keyboard?

Screenshot showing the bottom right 'done' button on the software keyboard

d-man
  • 57,473
  • 85
  • 212
  • 296

10 Answers10

233

I ended up with a combination of Roberts and chirags answers:

((EditText)findViewById(R.id.search_field)).setOnEditorActionListener(
        new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        // Identifier of the action. This will be either the identifier you supplied,
        // or EditorInfo.IME_NULL if being called due to the enter key being pressed.
        if (actionId == EditorInfo.IME_ACTION_SEARCH
                || actionId == EditorInfo.IME_ACTION_DONE
                || event.getAction() == KeyEvent.ACTION_DOWN
                && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
            onSearchAction(v);
            return true;
        }
        // Return true if you have consumed the action, else false.
        return false;
    }
});

Update: The above code would some times activate the callback twice. Instead I've opted for the following code, which I got from the Google chat clients:

public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    // If triggered by an enter key, this is the event; otherwise, this is null.
    if (event != null) {
        // if shift key is down, then we want to insert the '\n' char in the TextView;
        // otherwise, the default action is to send the message.
        if (!event.isShiftPressed()) {
            if (isPreparedForSending()) {
                confirmSendMessageIfNeeded();
            }
            return true;
        }
        return false;
    }

    if (isPreparedForSending()) {
        confirmSendMessageIfNeeded();
    }
    return true;
}
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
  • 5
    I just looked for IME_ACTION_DONE and I was surprised that it didn't trigger. After I also looked for ACTION_DOWN and KEYCODE_ENTER it finally triggered onEditorAction(). Since I see no difference in the built-in keyboard (I expected the Enter key to be highlighted) I wonder what the point is of using android:imeOptions="actionSend" for the EditText XML layout. – Someone Somewhere May 15 '11 at 01:53
  • why isn't this answer accepted. does this fail in few cases?? – Archie.bpgc Sep 27 '12 at 12:11
  • But this can't work as multiline textView!,In this U can't create the multiline TextView witch using in chat.. – Zala Janaksinh Jan 01 '14 at 04:57
  • 1
    http://developer.android.com/reference/android/widget/TextView.OnEditorActionListener.html (Description of what an 'event' is affirms same.) – Darpan Aug 07 '14 at 11:17
  • 2
    The second solution is also triggered twice. – Bagusflyer Sep 19 '14 at 12:56
  • 2
    What is `isPreparedForSending()` and why the second method returns `true`? – CoolMind Oct 02 '18 at 15:28
  • @CoolMind It is probably a function to check against invalid strings before sending. IE, is the text in the EditText null / empty. Is it at least 2 characters, etc. Just a guess, but it makes logical sense. – PGMacDesign Jan 05 '21 at 21:49
140

Try this, it should work for what you need:


editText.setOnEditorActionListener(new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    if (actionId == EditorInfo.IME_ACTION_DONE) {
       //do here your stuff f
       return true;
    }
    return false;
    } 
});
Hasan El-Hefnawy
  • 1,249
  • 1
  • 14
  • 20
chikka.anddev
  • 9,569
  • 7
  • 38
  • 46
  • 11
    This does not work for HTC Evo. As far as I've been able to understand, HTC implemented their own soft keyboard which ignores the imeOptions. – Dan May 16 '11 at 16:13
  • This suffices (no need to look at event action or key code, as in accepted answer); works on my Nexus and Samsung test devices. – Jonik Dec 07 '16 at 13:08
  • 2
    just to be safe, make sure that action in the code and the view matches ```` – bh_earth0 May 26 '18 at 08:32
53
<EditText android:imeOptions="actionDone"
          android:inputType="text"/>

The Java code is:

edittext.setOnEditorActionListener(new OnEditorActionListener() { 
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_DONE) {
            Log.i(TAG,"Here you can write the code");
            return true;
        }    
        return false;
    }
});
Andre Thiele
  • 3,202
  • 3
  • 20
  • 43
Vinoj Vetha
  • 726
  • 6
  • 8
52

Kotlin Solution

The base way to handle it in Kotlin is:

edittext.setOnEditorActionListener { _, actionId, _ ->
    if (actionId == EditorInfo.IME_ACTION_DONE) {
        callback.invoke()
        return@setOnEditorActionListener true
    }
    false
}

Kotlin Extension

Use this to just call edittext.onDone{/*action*/} in your main code. Makes your code far more readable and maintainable

fun EditText.onDone(callback: () -> Unit) {
    setOnEditorActionListener { _, actionId, _ ->
        if (actionId == EditorInfo.IME_ACTION_DONE) {
            callback.invoke()
            return@setOnEditorActionListener true
        }
        false
    }
}

Don't forget to add these options to your edittext

<EditText ...
    android:imeOptions="actionDone"
    android:inputType="text"/>

If you need inputType="textMultiLine" support, read this post

Tomas
  • 4,652
  • 6
  • 31
  • 37
Gibolt
  • 42,564
  • 15
  • 187
  • 127
  • 2
    Great answer, thanks for sharing! I seem to keep getting a lint warning regarding the return value of the `setOnEditorActionListener`, though. Maybe it's just a matter of local configuration settings, but my linter really, _really_ wants me to add an `else` branch too in order to accept the "true" as a return statement of the listener (as opposed to the `if`-block). – dbm Apr 02 '20 at 08:39
  • 1
    You need the ``else`` otherwise it will always return ``false``. A lambda is an expression, the result is always the last value expressed. Even if you go through the ``if`` branch and express ``true``, you immediately move on to the next line and express ``false`` - so that's always your result! Having an ``if-else`` as the last statement means you go down one branch or the other, and it's the last thing you do in the lambda, so you return the result of that branch – cactustictacs Sep 21 '20 at 12:45
  • 3
    You've an error for the lambda. Instead of `true` you should write `return@setOnEditorActionListener true`, otherwise it will go down to `false` in all cases. – Eugene Kartoyev Oct 05 '20 at 20:43
  • I'm confused about using return@setOnEditorActionListener true and then returning false without "return@setOnEditorActionListener" – David Jul 13 '22 at 20:09
27

I know this question is old, but I want to point out what worked for me.

I tried using the sample code from the Android Developers website (shown below), but it didn't work. So I checked the EditorInfo class, and I realized that the IME_ACTION_SEND integer value was specified as 0x00000004.

Sample code from Android Developers:

editTextEmail = (EditText) findViewById(R.id.editTextEmail);
editTextEmail
        .setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId,
                    KeyEvent event) {
                boolean handled = false;
                if (actionId == EditorInfo.IME_ACTION_SEND) {
                    /* handle action here */
                    handled = true;
                }
                return handled;
            }
        });

So, I added the integer value to my res/values/integers.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="send">0x00000004</integer>
</resources>

Then, I edited my layout file res/layouts/activity_home.xml as follows

<EditText android:id="@+id/editTextEmail"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:imeActionId="@integer/send"
  android:imeActionLabel="@+string/send_label"
  android:imeOptions="actionSend"
  android:inputType="textEmailAddress"/>

And then, the sample code worked.

iRuth
  • 2,737
  • 3
  • 27
  • 32
18

More details on how to set the OnKeyListener, and have it listen for the Done button.

First add OnKeyListener to the implements section of your class. Then add the function defined in the OnKeyListener interface:

/*
 * Respond to soft keyboard events, look for the DONE press on the password field.
 */
public boolean onKey(View v, int keyCode, KeyEvent event)
{
    if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
        (keyCode == KeyEvent.KEYCODE_ENTER))
    {
        // Done pressed!  Do something here.
    }
    // Returning false allows other listeners to react to the press.
    return false;
}

Given an EditText object:

EditText textField = (EditText)findViewById(R.id.MyEditText);
textField.setOnKeyListener(this);
Robert Hawkey
  • 761
  • 7
  • 12
  • 2
    This doesn't work any more on applications targeting API level 17+, because your OnKeyListener is no longer fired for soft-key events: http://developer.android.com/reference/android/text/method/KeyListener.html – jjb Dec 13 '13 at 18:14
  • Do they suggest an alternative now? – Robert Hawkey Dec 16 '13 at 14:53
  • 2
    I've switched to using setOnEditorActionListener to look for EditorInfo.IME_ACTION_DONE, which seems to work well. – jjb Dec 16 '13 at 19:09
  • onKey should return true, not false. – ralphgabb Jan 27 '15 at 06:45
17

While most people have answered the question directly, I wanted to elaborate more on the concept behind it. First, I was drawn to the attention of IME when I created a default Login Activity. It generated some code for me which included the following:

<EditText
  android:id="@+id/password"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:hint="@string/prompt_password"
  android:imeActionId="@+id/login"
  android:imeActionLabel="@string/action_sign_in_short"
  android:imeOptions="actionUnspecified"
  android:inputType="textPassword"
  android:maxLines="1"
  android:singleLine="true"/>

You should already be familiar with the inputType attribute. This just informs Android the type of text expected such as an email address, password or phone number. The full list of possible values can be found here.

It was, however, the attribute imeOptions="actionUnspecified" that I didn't understand its purpose. Android allows you to interact with the keyboard that pops up from bottom of screen when text is selected using the InputMethodManager. On the bottom corner of the keyboard, there is a button, typically it says "Next" or "Done", depending on the current text field. Android allows you to customize this using android:imeOptions. You can specify a "Send" button or "Next" button. The full list can be found here.

With that, you can then listen for presses on the action button by defining a TextView.OnEditorActionListener for the EditText element. As in your example:

editText.setOnEditorActionListener(new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(EditText v, int actionId, KeyEvent event) {
    if (actionId == EditorInfo.IME_ACTION_DONE) {
       //do here your stuff f
       return true;
    }
    return false;
    } 
});

Now in my example I had android:imeOptions="actionUnspecified" attribute. This is useful when you want to try to login a user when they press the enter key. In your Activity, you can detect this tag and then attempt the login:

    mPasswordView = (EditText) findViewById(R.id.password);
    mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
            if (id == R.id.login || id == EditorInfo.IME_NULL) {
                attemptLogin();
                return true;
            }
            return false;
        }
    });
Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101
12

Thanks to chikka.anddev and Alex Cohn in Kotlin it is:

text.setOnEditorActionListener { v, actionId, event ->
    if (actionId == EditorInfo.IME_ACTION_DONE ||
        event?.action == KeyEvent.ACTION_DOWN && event.keyCode == KeyEvent.KEYCODE_ENTER) {
        doSomething()
        true
    } else {
        false
    }
}

Here I check for Enter key, because it returns EditorInfo.IME_NULL instead of IME_ACTION_DONE.

See also Android imeOptions="actionDone" not working. Add android:singleLine="true" in the EditText.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
3

first level : First, you must specify the following attributes for the action to be able to apply the appearance changes to the keyboard.

<androidx.appcompat.widget.AppCompatEditText
        android:imeOptions="actionDone"
        android:inputType="phone"
        android:maxLength="11"
        android:lines="1" />

second level:

phoneNumberET.setOnEditorActionListener(object: OnEditorActionListener {
        override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
            if(EditorInfo.IME_ACTION_DONE == actionId){
                handleLogin()
                return true
            }
            return false
        }
    })
7Cloner
  • 51
  • 1
0

If you use Android Annotations https://github.com/androidannotations/androidannotations

You can use @EditorAction annotation

@EditorAction(R.id.your_component_id)
void onDoneAction(EditText view, int actionId){
    if(actionId == EditorInfo.IME_ACTION_DONE){
        //Todo: Do your work or call a method
    }
}
Rafael Rocha
  • 285
  • 3
  • 5