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.