Android SoftKeyboard onKeyDown/Up not detecting &#

2019-01-26 20:59发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

Got it :)

public boolean onKey(View v, int keyCode, KeyEvent event) {
    if(event.getAction()==KeyEvent.ACTION_DOWN) return true;
    if(keyCode==KeyEvent.KEYCODE_ALT_LEFT || keyCode==KeyEvent.KEYCODE_ALT_RIGHT || keyCode==KeyEvent.KEYCODE_SHIFT_LEFT || keyCode==KeyEvent.KEYCODE_SHIFT_RIGHT) return true;

    if (keyCode == KeyEvent.KEYCODE_DEL) {
        doBackspace();
        return true;
    } else if (keyCode == KeyEvent.KEYCODE_BACK) {
        if(this.avl!=null) this.avl.onInputCancelled(this);
        return false;
    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
        inputstarted=false;
        if(this.avl!=null) this.avl.onInputFinished(this,this.text,celldata);
        return true;
    }   

    String key = "";
    if (event.getAction()==KeyEvent.ACTION_UP) key = String.valueOf((char)event.getUnicodeChar()).toUpperCase();
    else if (event.getAction()==KeyEvent.ACTION_MULTIPLE) key = String.valueOf(event.getCharacters()).toUpperCase();
    return process(key);
}

The important part is blocking the ALT_LEFT/SHIFT_LEFT/etc keycodes and differentiate between ACTION_UP/ACTION_MULTIPLE and either use event.getUnicodeChar() or event.getCharacters().

It works now, I can get all of the chars and even KEYCODE_DEL works on the mobile. But on the tablet I still get no callback for the delete key. Seems like a bad bug, as today in the morning it worked fine even on tablet.