对Android 软键盘向下的监听

因为业务需要,有时候我们好监听软键盘向下的动作,当我们按下向下的按钮时,可以进行监听,从而执行相应的动作。
对Android 软键盘向下的监听_第1张图片

于是我们写下下面的代码

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

在布局中


<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>

对Android 软键盘向下的监听_第2张图片

点击向下后,可以看到 log

对Android 软键盘向下的监听_第3张图片

这是键盘向下的监听,我们得到了,然后可以执行相应的动作。

作为一个程序员,我们不仅要知道怎样做,还应该知道为什么会这样。这个问题就涉及到 Android 输入系统,详情见上一篇博文 Android 输入系统

为什么 重写 Activity.onKeyDown() 方法为什么没有用?

根据 Android 输入系统 一文,我们知道事件的流水线处理顺序 对Android 软键盘向下的监听_第4张图片

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() 方法,在这个方法里进行监听了。

你可能感兴趣的:(Android)