安卓软键盘的onKeyDown/Up无法检测到“备选”按键

5

我有一个视图,负责为我处理输入。我弹出了一个键盘并将视图设置为可聚焦。现在我可以获取某些按键。

@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 {
    }
}

等等……我使用以下方式得到按下的字符

event.getDisplayLabel()

只有在我只想要正常的A-Z字母时才有效。在其他语言中,通过长按软键盘上的普通字母可以访问更多字母...但是,这些替代字母无法被onKeyDown/Up检测到。我只能检测到普通字母,也就是软键盘上的标签。 现在我的应用程序必须处理外文输入和字母,我已将键盘更改为土耳其语,并且我可以在键盘上找到像í ì ú ù这样的字母,但是如果我按下它们,我得不到任何响应。既不能使用event.getDisplayLabel也不能使用event.getUnicodeChar()。 如何检测这些字母?

在同一帖子中提供可能的解决方案:https://dev59.com/J2sz5IYBdhLWcg3wpZlF创建具有每个键多个备选字符的软键盘。 - Gridtestmail
嗯...不行吗?键盘可以使用,它是标准的安卓键盘。我不想创建一个新的键盘,也不想添加任何新的字母、布局或功能。我只想检测已经存在的字母,这些字母由标准键盘提供。我可以检测到A-Z(基本上所有ASCII码),但不能检测到特殊字符如ìíùú等,因为它们似乎无法通过onKeyUp/Down获取。 - El Duderino
3个回答

3
当键盘打开时,onKeyDown()onKeyUp()方法无法正常工作,因为Android将屏幕键盘视为单独的活动。
实现您想要的最简单的方法是在视图上覆盖onKeyPreIme()方法。例如,如果您尝试从EditText捕获onKeyDown,则创建一个新类,该类扩展EditText,并覆盖onKeyPreIme()方法即可:
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;
    }
}

这是在安卓4.4 / HTC One上测试的。


1
我是Android的新手。我该如何使用LoseFocusEditText使我的应用程序在按键按下时触发onKeyPreIme? - keinabel

2

编辑:

我不确定是什么原因导致onKeyDown根本没有被调用。可能是你的视图有问题吗?由于这个原因,可能会有比我的解决方案更好的答案。无论如何,这个方法可能有效:

不要使用视图中的onKeyDown,而是在activity级别上覆盖dispatchKeyEvent。这将在键事件到达窗口管理器之前处理您的键事件,因此确保对任何您未明确处理的键事件调用super。

例如,使用ACTION_DOWN(因为每个事件都有ACTION_UP和ACTION_DOWN),因为您的示例使用了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(抱歉)

您可以尝试:

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

与其

char key = event.getDisplayLabel();

getDisplayLabel()只会给你显示在键盘上的键名,正如你所指出的那样,这不一定是用户选择的字符。


不,我在我的初始帖子中写道我已经尝试过了,这不是解决方案。问题是,如果我选择其中一个替代字符,onKeyDown/onKeyUp就不会被触发。监听器根本没有触发。 - El Duderino
好的。抱歉。我编辑了答案。如果这个解决方案更好,请告诉我。 - jacobhyphenated
嘿,谢谢回复。与此同时,我设法解决了问题,我将所有内容从onKeyDown移动到onKey(使用OnKeyListener),并包括以下内容:如果(event.getAction()==KeyEvent.ACTION_UP)key = String.valueOf(event.getDisplayLabel()).toUpperCase(); else if (event.getAction()==KeyEvent.ACTION_MULTIPLE) key = String.valueOf(event.getCharacters()).toUpperCase(); 现在我可以获取所有字符,一切正常...除了一件事。在平板电脑上无法检测到KEYCODE_DEL...它在手机上可以工作,但在平板电脑上不行。这很奇怪,因为在我移动到onKey之前它是可以工作的。 - El Duderino

1

明白了 :)

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

重要的部分是阻止ALT_LEFT/SHIFT_LEFT等键码,并区分ACTION_UP/ACTION_MULTIPLE,然后使用event.getUnicodeChar()或event.getCharacters()。
现在它可以工作了,我可以获取所有字符,甚至在移动设备上KEYCODE_DEL也可以正常工作。但在平板电脑上,我仍然无法回调删除键。似乎是一个严重的错误,因为今天早上即使在平板电脑上也可以正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接