因为业务需要,有时候我们好监听软键盘向下的动作,当我们按下向下的按钮时,可以进行监听,从而执行相应的动作。
于是我们写下下面的代码
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK){
- Log.i(TAG, "onKeyDown");
- }
- return super.onKeyDown(keyCode, event);
- }
我们按下向下的按钮,滴,没反应。再按一次,滴,握草,直接退出了,这个时候才有 log 打印出来。这个 log 是退出当前 Activity, 而不是软键盘向下的。
如果没有想到好的解决方案,就问 google , 于是我们得到了解决方法,就是对 Edittext.onKeyPreIme() 方法重写
- /**
- * 拦截键盘向下的 EditTextView
- */
- public class TextEditTextView extends EditText {
- public TextEditTextView(Context context) {
- super(context);
- }
- public TextEditTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- public TextEditTextView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == 1) {
- Log.i("main_activity", "键盘向下 ");
- super.onKeyPreIme(keyCode, event);
- return false;
- }
- return super.onKeyPreIme(keyCode, event);
- }
- }
在布局中
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout
- android:id="@+id/activity_main"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/white">
- <com.yxhuang.myapplication.TextEditTextView
- android:id="@+id/edt_text"
- android:layout_width="match_parent"
- android:hint="测试"
- android:layout_marginTop="30dp"
- android:textColorHint="@android:color/black"
- android:layout_height="wrap_content"/>
- </RelativeLayout>
点击向下后,可以看到 log
这是键盘向下的监听,我们得到了,然后可以执行相应的动作。
作为一个程序员,我们不仅要知道怎样做,还应该知道为什么会这样。这个问题就涉及到 Android 输入系统,详情见上一篇博文
为什么 重写 Activity.onKeyDown() 方法为什么没有用?
根据 一文,我们知道事件的流水线处理顺序
Ime 是键盘输入法, ViewPreImeStage 是在输入法之前,ImeStage 是输入法处理,ViewPostImeStage 才是最终传递到 Activity.onKeyDown() 的。
ViewPreImeStage 中可以将键盘向下的事件传递到 View, 而到了 ViewPostImeStage 则没有,那应该是有东西消耗了这个事件。当我们按下向下的按钮,键盘会收起来,显然是键盘消耗了这个事件。
我们看看 ImeStage 的源码
- /**
- * Delivers input events to the ime.
- * Does not support pointer events.
- */
- final class ImeInputStage extends AsyncInputStage
- implements InputMethodManager.FinishedInputEventCallback {
- public ImeInputStage(InputStage next, String traceCounter) {
- super(next, traceCounter);
- }
- @Override
- protected int onProcess(QueuedInputEvent q) {
- if (mLastWasImTarget) {
- // 获取 InputMethodManager 实例
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- final InputEvent event = q.mEvent;
- // dispatchInputEvent 方法处理了事件
- int result = imm.dispatchInputEvent(event, q, this, mHandler);
- if (result == InputMethodManager.DISPATCH_HANDLED) {
- return FINISH_HANDLED;
- } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
- return FINISH_NOT_HANDLED;
- } else {
- return DEFER; // callback will be invoked later
- }
- }
- }
- return FORWARD;
- }
- @Override
- public void onFinishedInputEvent(Object token, boolean handled) {
- QueuedInputEvent q = (QueuedInputEvent)token;
- if (handled) {
- finish(q, true);
- return;
- }
- forward(q);
- }
- }
我们看到如果 InputMethodeManager.dispatchInputEvent(…) 方法返回的结果是
InputMethodManager.DISPATCH_HANDLED 则结束事件的传递。事件就传递不到 ViewPostImeStage, 从而也就传递不到 Activity.onKeyDown() 中了。
到这来我们终于知道了为什么重写 Activity.onKeyDown() 方法没用了,想要监听键盘向下的事件,需要重写 Edittext.onKeyPreime() 方法,在这个方法里进行监听了。
来源: