I have an edit text which functions as a search box in my application. In Jelly Bean on my Nexus 7 when I type something into the text box which I am listening on and hit enter the KeyEvent = null and ActionId = 0 passed into the onEditorAction() method. Has anyone else encountered this? I'm thinking it might be a bug.
In the second if statement below I get a null pointer because the actionId = 0 and KeyEvent = null;
// Search field logic.
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Log.d(TAG, "onEditorAction");
if (event != null && event.getAction() != KeyEvent.ACTION_DOWN)
return false;
if (actionId == EditorInfo.IME_ACTION_SEARCH
|| event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
.....Do some stuff();
}
}
Ended up adding in a null check for KeyEvent. Thanks to commonsware for pointing out this happens on 3.0+. Seems more like a workaround then a solution, but it works.
// Search field logic.
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Log.d(TAG, "onEditorAction");
if (event != null && event.getAction() != KeyEvent.ACTION_DOWN) {
return false;
} else if (actionId == EditorInfo.IME_ACTION_SEARCH
|| event == null
|| event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
.....Do some stuff();
}
}
I found that my "bug-like behavior" was due to imeActionLabel complicating things. I only used it because it was mentioned in the Text Fields Guide as a way to have a custom return key label. Here are the results of my tests in Lollipop,
Case 1: default, return key symbol = closing angle bracket
<EditText
android:singleLine="true"
android:inputType="textUri"/>
onEditorAction is called once.
KeyEvent = null, actionId = 5 = EditorInfo.IME_ACTION_NEXT
if return true, cursor remains in EditText, keyboard open
if return false, cursor moves to next focusable, keyboard open if
necessary
Case 2: imeOptions, return key symbol = checkmark
<EditText
android:singleLine="true"
android:inputType="textUri"
android:imeOptions="actionDone"/>
onEditorAction is called once.
KeyEvent = null, actionId = 6 = EditorInfo.IME_ACTION_DONE
if return true, cursor remains in EditText, keyboard open
if return false, cursor remains in EditText, keyboard closes
Case 3: imeActionLabel, return key symbol = "URdone"
<EditText
android:singleLine="true"
android:inputType="textUri"
android:imeOptions="actionDone"
android:imeActionLabel="URdone"/>
onEditorAction can be called more than once.
KeyEvent = null, actionId = 0
if return true, cursor remains in EditText, keyboard open, onEditorAction is NOT called a second time
if return false, onEditorAction is called a SECOND time:
KeyEvent = KeyEvent.ACTION_DOWN, actionId = 0
if return false, cursor moves to next focusable, keyboard open if necessary, onEditorAction is NOT called a third time
if return true, onEditorAction is called a THIRD time:
KeyEvent = KeyEvent.ACTION_UP, actionId = 0
if return true, cursor remains in EditText, keyboard open
if return false, cursor moves to next focusable, keyboard open if necessary
NOTES:
I'm not sure if actionId = 0 is from EditorInfo.IME_ACTION_UNSPECIFIED or EditorInfo.IME_NULL.
If the next focusable is non-editable, the return key symbol becomes a left pointing arrow.
You can also use setOnFocusChangeListener to override onFocusChange, which will be called according to the above cursor behavior.
Beside KeyEvent.ACTION_UP we also need to capture KeyEvent.ACTION_DOWN. Unless KeyEvent.ACTION_UP will never be passed to EditText so our onEditorAction will not work.
Example:
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
final boolean isEnterEvent = event != null
&& event.getKeyCode() == KeyEvent.KEYCODE_ENTER;
final boolean isEnterUpEvent = isEnterEvent && event.getAction() == KeyEvent.ACTION_UP;
final boolean isEnterDownEvent = isEnterEvent && event.getAction() == KeyEvent.ACTION_DOWN;
if (actionId == EditorInfo.IME_ACTION_DONE || isEnterUpEvent ) {
// Do your action here
performLogin();
return true;
} else if (isEnterDownEvent) {
// Capture this event to receive ACTION_UP
return true;
} else {
// We do not care on other actions
return false;
}
}
You have to replace EditorInfo.IME_ACTION_DONE to correct version of EditorInfo.IME_ACTION_ according to android:imeOptions="actionNext"
It might be worth noting, that you can get more than one event for the click on Enter (depending on the android version). One for the KeyDown (KeyEvent.ACTION_DOWN), one for the KeyUp (KeyEvent.ACTION_UP). When I forgot to check that I accidentally started two server calls for the same action.
searchBox.setOnEditorActionListener(new OnEditorActionListener() {
// enter key in search box triggers search
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if ((event != null && event.getAction() == KeyEvent.ACTION_UP) || event==null) {
onSearchButtonClicked();
}
return true;
}
});
You dont discover the truth, if you customize return key. You need both set imeActionLabel and imeActionId in your layout. Such as:
imeActionLabel="xxxx"
imeActionId = "6"
In your java code:
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
doSomeThing();
return true;
}
return false;
}
It will work fine.
The action id is set to 0 by default for any enter event.
From the Android documentation:
actionId
int: 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.
So the proper way to handle enter key events would be:
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_NULL) {
// Handle return key here
return true;
}
return false;
}
Related
I have a KeyListener on an editText like so:
tip = (EditText) findViewById(R.id.tip);
tip.setOnKeyListener(new EditText.OnKeyListener(){
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.i("debug123", "onKeyListener. event.getKeyCode(): " + event.getKeyCode());
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
checkInput();
return true;
}
return false;
}
});
But no softkeyboard-stroke is recognised.?
Only when I left the Activity with the BACK-Button (HardwareButton) the Listener recognises the action.
But from all that I read, this is the way to go if I want to work with user-input at a EditText.
setOnKeyListener
Register a callback to be invoked when a hardware key is pressed in
this view. Key presses in software input methods will generally not
trigger the methods of this listener.
setOnEditorActionListener
Set a special listener to be called when an action is performed on the
text view. This will be called when the enter key is pressed, or when
an action supplied to the IME is selected by the user.
To solve your problem using setOnEditorActionListener, please check below:
Add imeOptions and inputType to your EditText
<EditText
android:id="#+id/tip"
android:imeOptions="actionDone"
android:inputType="text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Then add setOnEditorActionListener to EditText
tip.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ( (actionId == EditorInfo.IME_ACTION_DONE) || (event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) ) {
checkInput();
return true;
} else {
return false;
}
}
});
Here,
actionId == EditorInfo.IME_ACTION_DONE handle action from Soft Keyboard (IME)
event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN handle enter key from Hardware Keyboard
My application works with remote Bluetooth keyboard. I have defined some predefined custom implementation on arrow keys and enter key. I want to perform only those actions whenever user press arrow key or Enter Key in my application.
But sometimes it is still showing default behavior of keys.
My Current Code is as follows
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
// my custom work
return true;
}else{
return super.onKeyUp(keyCode, event);
}
}
any idea, how I can restrict default behavior of arrow keys and enter key?
Thanks
Use below code:
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KEYCODE_DPAD_RIGHT || keyCode == KEYCODE_ENTER) {
// my custom work
return false;
}else{
return super.onKeyUp(keyCode, event);
}
}
I am trying to programmatically designate where the focus goes when a user presses the next button on the keyboard. I can get it to work for some, but I have one case where the view gets skipped and the view below it gets the focus.
I went into debugger and the focus does go through my view, but then jumps to the last one. Below is a small diagram of what I'm talking about.
(1) EditText
(2) AutoCompleteEditText
(3) EditText
(4) EditText
(1) -> (2) is ok
(2) -> (3) doesn't work, it does (2) -> (3) -> (4), where it briefly go to (3)
if I start at (3), then (3) -> (4) is ok.
This is how I am setting the focus
view.setOnKeyListener(new OnKeyListener()
{
#Override
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER))
{
view.clearFocus();
nextView.requestFocus();
return true;
}
return false;
}
});
view.setOnKeyListener(new OnKeyListener()
{
#Override
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if((event.getAction() == KeyEvent.ACTION_UP) && (keyCode == KeyEvent.KEYCODE_ENTER))
{
view.clearFocus();
nextView.requestFocus();
return true;
}
return false;
}
});
Found the solution. If anyone else is having this issue, just change ACTION_DOWN to ACTION_UP so your code is only triggered when the press is complete. I'm guessing with ACTION_DOWN, there are multiple calls made. But I'm not sure.
Hello
I've got a searched EditText and search Button. When I type the searched text, I'd like to use ENTER key on softkeyboard instead of search Button to activate search function.
Thanks for help in advance.
You do it by setting a OnKeyListener on your EditText.
Here is a sample from my own code. I have an EditText named addCourseText, which will call the function addCourseFromTextBox when either the enter key or the d-pad is clicked.
addCourseText = (EditText) findViewById(R.id.clEtAddCourse);
addCourseText.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_DOWN)
{
switch (keyCode)
{
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
addCourseFromTextBox();
return true;
default:
break;
}
}
return false;
}
});
<EditText
android:id="#+id/search"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="#string/search_hint"
android:inputType="text"
android:imeOptions="actionSend" />
You can then listen for presses on the action button by defining a TextView.OnEditorActionListener for the EditText element. In your listener, respond to the appropriate IME action ID defined in the EditorInfo class, such as IME_ACTION_SEND. For example:
EditText editText = (EditText) findViewById(R.id.search);
editText.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
boolean handled = false;
if (actionId == EditorInfo.IME_ACTION_SEND) {
sendMessage();
handled = true;
}
return handled;
}
});
Source: https://developer.android.com/training/keyboard-input/style.html
may be you could add a attribute to your EditText like this:
android:imeOptions="actionSearch"
add an attribute to the EditText like
android:imeOptions="actionSearch"
this is the best way to do the function
and the imeOptions also have some other values like "go" 、"next"、"done" etc.
Most updated way to achieve this is:
Add this to your EditText in XML:
android:imeOptions="actionSearch"
Then in your Activity/Fragment:
EditText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// Do what you want here
return#setOnEditorActionListener true
}
return#setOnEditorActionListener false
}
We can also use Kotlin lambda
editText.setOnKeyListener { _, keyCode, keyEvent ->
if (keyEvent.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
Log.d("Android view component", "Enter button was pressed")
return#setOnKeyListener true
}
return#setOnKeyListener false
}
To avoid the focus advancing to the next editable field (if you have one) you might want to ignore the key-down events, but handle key-up events. I also prefer to filter first on the keyCode, assuming that it would be marginally more efficient. By the way, remember that returning true means that you have handled the event, so no other listener will. Anyway, here is my version.
ETFind.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
|| keyCode == KeyEvent.KEYCODE_ENTER) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
// do nothing yet
} else if (event.getAction() == KeyEvent.ACTION_UP) {
findForward();
} // is there any other option here?...
// Regardless of what we did above,
// we do not want to propagate the Enter key up
// since it was our task to handle it.
return true;
} else {
// it is not an Enter key - let others handle the event
return false;
}
}
});
this is a sample of one of my app how i handle
//searching for the Edit Text in the view
final EditText myEditText =(EditText)view.findViewById(R.id.myEditText);
myEditText.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN)
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER) ||
(keyCode == KeyEvent.KEYCODE_ENTER)) {
//do something
//true because you handle the event
return true;
}
return false;
}
});
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?
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;
}
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;
}
});
<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;
}
});
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
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.
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);
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;
}
});
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.
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
}
}