null keyevent and actionid = 0 in onEditorAction()

2019-01-14 15:23发布

问题:

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();
     }
}

回答1:

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();
    }
}


回答2:

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.



回答3:

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"



回答4:

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;
}
});


回答5:

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.