Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android SoftKeyboard onKeyDown/Up not detecting 'alternative' keys

I have a view which handles input for me, I pop up a keyboard and set the view focusable. Now I can get certain key presses...

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_DEL) {
    } else if (keyCode == KeyEvent.KEYCODE_BACK) {
    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
    } else {
    }
}

and so on... the character pressed I get by using

event.getDisplayLabel()

That works as long as I only want the normal letters A-Z. In other languages, more letters can be reached by long pressing a normal letter on the soft keyboard... however, these alternative letters cannot be detected by onKeyDown/Up. I can only detect the normal letters, the labels of the soft keyboard. Now my app has to process foreign input and letters, I have changed the keyboard to turkish and I can find letters like í ì ú ù on the keyboard, but if I press them, I don't get any response. Not with event.getDisplayLabel nor event.getUnicodeChar(); How do I detect these letters?

like image 813
El Duderino Avatar asked Jan 08 '13 14:01

El Duderino


2 Answers

When the keyboard is open, onKeyDown() and onKeyUp() methods don't work properly because Android considers on-screen keyboard as a separate activity.

The easiest way to achieve what you want is to override onKeyPreIme() method on your view. For example, if you're trying to capture onKeyDown from an EditText, create a new Class which extends EditText, and override the onKeyPreIme() method:

public class LoseFocusEditText extends EditText {

    private Context mContext;

    protected final String TAG = getClass().getName();

    public LoseFocusEditText(Context context) {
        super(context);
        mContext = context;
    }

    public LoseFocusEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
    }

    public LoseFocusEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {

            //hide keyboard
            InputMethodManager mgr = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
            mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);

            //lose focus
            this.clearFocus();

            return true;
        }
        return false;
    }
}

This was tested on kitkat / htc one.

like image 134
Eduard Avatar answered Sep 27 '22 19:09

Eduard


Edit:

I'm not sure what is causing the onKeyDown not to be called at all. Perhaps it's an issue with your view? There may be a better answer than my solution because of this. Either way, this might work:

Instead of using the onKeyDown from the view, override dispatchKeyEvent at the activity level. This will handle your key event before it gets to the window manager, so make sure to call super on any key events you do not explicitly handle.

Example using the ACTION_DOWN (as every event has both ACTION_UP and ACTION_DOWN) since your example used onKeyDown:

@Override
public boolean dispatchKeyEvent(KeyEvent event){
    if(event.getAction() == KeyEvent.ACTION_UP) {
        return super.dispatchKeyEvent(event);
    }

    if (keyCode == KeyEvent.KEYCODE_DEL) {
    } else if (keyCode == KeyEvent.KEYCODE_BACK) {
    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
    } else {
        return super.dispatchKeyEvent(event);
    }
}

NOW (sorry about that)

You can try:

char key = (char)event.getUnicodeChar();

Instead of

char key = event.getDisplayLabel();

getDisplayLabel() will only give you the key that is displayed on the keyboard, which as you point out, is not necessarily the character the user has selected.

like image 20
jacobhyphenated Avatar answered Sep 27 '22 20:09

jacobhyphenated