On just one phone I am testing on (HTC Incredible, Android 2.2, Software 3.21.605.1), I am experiencing the following behavior.
The onEditorAction event handler is being called twice (immediately) when the Enter key on the Sense UI keyboard is pressed.
The KeyEvent.getEventTime() is the same for both times the event is called, leading me to this work-around:
protected void onCreate(Bundle savedInstanceState) {
[...]
EditText text = (EditText)findViewById(R.id.txtBox);
text.setOnEditorActionListener(new OnEditorActionListener() {
private long lastCalled = -1;
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ( event.getEventTime() == lastCalled ) {
return false;
} else {
lastCalled = event.getEventTime();
handleNextButton(v);
return true;
}
}
});
[...]
}
The EditText is defined as:
<EditText
android:layout_width="150sp"
android:layout_height="wrap_content"
android:id="@+id/txtBox"
android:imeOptions="actionNext"
android:capitalize="characters"
android:singleLine="true"
android:inputType="textVisiblePassword|textCapCharacters|textNoSuggestions"
android:autoText="false"
android:editable="true"
android:maxLength="6"
/>
On all other devices I've tested on, the action button is properly labeled "Next" and the event is only called a single time when that button is pressed.
Is this a bug in Sense UI's keyboard, or am I doing something incorrectly?
Thank you for any assistance.
Updated - thanks to the answers given, I have settled on the following as my checks. This works fine on both of the phones I have available to test (Sense UI and Cyanogenmod CM7)
if (event != null && event.getAction() != KeyEvent.ACTION_DOWN) {
return false;
}
if ( actionId != EditorInfo.IME_ACTION_NEXT && actionId != EditorInfo.IME_NULL ) {
return false;
}
As mitch said, you have to check the event action:
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (event == null || event.getAction() != KeyEvent.ACTION_DOWN)
return false;
// do your stuff
return true;
}
This snippet works on both the Sense UI and the emulator.
(EditText) passwordView = (EditText) findViewById(R.id.password);
passwordView.setImeOptions(EditorInfo.IME_ACTION_DONE);
passwordView.setOnEditorActionListener(new OnEditorActionListener()
{
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
String input;
if(actionId == EditorInfo.IME_ACTION_DONE)
{
input= v.getText().toString();
Toast toast= Toast.makeText(LogIn.this,input,
Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
return true;
}
return false;
}
});
I think if you return true, it means that you are interested in the rest of the events until it reaches IME_ACTION_DONE. So if you return false, it means that you are not interested in the events and that other views will then have the opportunity to handle it. Since you only have 1 view, I suggest you just ignore the actionId check and just return false every time.
etMovieName = (EditText) view.findViewById(R.id.et_movie_name);
etMovieName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView tv, int actionId, KeyEvent event) {
System.out.println("actionId= "+ actionId);
performSearch();
return false;
}
});
Other situation is if you have overlapping views or a sibling view, then you can use actionId to pass information around. In this situation, returning true will allow you to pass info to the other view. If you really are interested in the event/actionId (for example if you have another sibling view), then you can do this:
etMovieName = (EditText) view.findViewById(R.id.et_movie_name);
etMovieName.setImeOptions(EditorInfo.IME_ACTION_DONE);
etMovieName.setSingleLine();
etMovieName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView tv, int actionId, KeyEvent event) {
System.out.println("actionId= "+ actionId);
return performSearch(); }
});
Notice the actionId value has changed to 6 once I added:
etMovieName.setImeOptions(EditorInfo.IME_ACTION_DONE);
etMovieName.setSingleLine();
In addition to @Gubbel 's answer, I think listen to ACTION_UP
instead of ACTION_DOWN
is better, because user can actually tap his finger on the enter key (ACTION_DOWN
) but then hesitate and hold it to other places.
Normally we want to fire the event only when user release his finger from the enter key.
So the code will be like this:
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
boolean handled = false;
if (event.getAction() == KeyEvent.ACTION_DOWN) return true;
if ((actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL)
&& event.getAction() == KeyEvent.ACTION_UP) {
//Do your stuff here
handled = true;
}
return handled;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With