View的dispatchKeyEvent源码分析

来源:互联网 发布:网络诊断dns未响应win7 编辑:程序博客网 时间:2024/06/06 01:50

View的dispatchKeyEvent源码分析

github

目前还不明白的是:KeyEvent是怎么产生的?dispatchKeyEvent是由谁掉用的?所以这里仅对View中KeyEvent的dispatch过程进行分析。分析的源码始于View的dispatchKeyEvent函数(API-26)

Google的注释

先看一下Google对dispatchKeyEvent函数的注释

Dispatch a key event to the next view on the focus path. This path runs from the top of the view tree down to the currently focused view. If this view has focus, it will dispatch to itself. Otherwise it will dispatch the next node down the focus path. This method also fires any key listeners.

这里的focus path还不太明白是什么,整段注释大概的意思是key event会沿着focus path自顶向下传递,若当前view has focus,则把key event dispatch给自己,否则dispatch给focus path上的下一个节点。

源码跟踪

话不多说,先贴dispatchKeyEvent的整段源码

public boolean dispatchKeyEvent(KeyEvent event) {    if (mInputEventConsistencyVerifier != null) {        mInputEventConsistencyVerifier.onKeyEvent(event, 0);    }    // Give any attached key listener a first crack at the event.    //noinspection SimplifiableIfStatement    ListenerInfo li = mListenerInfo;    if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED            && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {        return true;    }    if (event.dispatch(this, mAttachInfo != null            ? mAttachInfo.mKeyDispatchState : null, this)) {        return true;    }    if (mInputEventConsistencyVerifier != null) {        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);    }    return false;}

首先会看View有没有set过OnKeyListener,如果有且View是ENABLED的,回调listener的onKey方法。如果onKey方法返回true,表明事件被消费了,则dispatchKeyEvent直接向上级返回true;否则,dispatchKeyEvent继续执行。

ListenerInfo li = mListenerInfo;if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED        && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {    return true;}

接下来会进入KeyEvent的dispatch函数,若该函数返回true,则dispatchKeyEvent直接向上级返回true;否则,dispatchKeyEvent继续执行。

if (event.dispatch(this, mAttachInfo != null        ? mAttachInfo.mKeyDispatchState : null, this)) {    return true;}

在dispatch里判断KeyEvent的Action类型,如果是ACTION_DOWN,View的onKeyDown方法会被回调;如果是ACTION_UP,View的onKeyUp方法会被回调。ACTION_MULTIPLE暂且还不知道是什么意思。要说明一下的是onKeyDown和onKeyUp都是KeyEvent的Callback接口里定义的函数,View实现了该接口。

public final boolean dispatch(Callback receiver, DispatcherState state,            Object target) {    switch (mAction) {        case ACTION_DOWN: {            mFlags &= ~FLAG_START_TRACKING;            if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state                    + ": " + this);            //回调onKeyDown            boolean res = receiver.onKeyDown(mKeyCode, this);            //state还没搞懂是干啥的,好像和LongPress有些关系            if (state != null) {                if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {                    if (DEBUG) Log.v(TAG, "  Start tracking!");                    state.startTracking(this, target);                } else if (isLongPress() && state.isTracking(this)) {                    try {                        if (receiver.onKeyLongPress(mKeyCode, this)) {                            if (DEBUG) Log.v(TAG, "  Clear from long press!");                            state.performedLongPress(this);                            res = true;                        }                    } catch (AbstractMethodError e) {                    }                }            }            return res;        }        case ACTION_UP:            if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state                    + ": " + this);            //state还没搞懂是干啥的            if (state != null) {                state.handleUpEvent(this);            }            //onKeyUp会被回调            return receiver.onKeyUp(mKeyCode, this);        //ACTION_MULTIPLE暂且还不知道是什么意思。        case ACTION_MULTIPLE:            final int count = mRepeatCount;            final int code = mKeyCode;            if (receiver.onKeyMultiple(code, count, this)) {                return true;            }            if (code != KeyEvent.KEYCODE_UNKNOWN) {                mAction = ACTION_DOWN;                mRepeatCount = 0;                boolean handled = receiver.onKeyDown(code, this);                if (handled) {                    mAction = ACTION_UP;                    receiver.onKeyUp(code, this);                }                mAction = ACTION_MULTIPLE;                mRepeatCount = count;                return handled;            }            return false;    }    return false;}

View的onKeyDown函数是这样实现的:首先确定按下的键是不是ConfirmKey,当KeyCode的值为以下的任一时,被认为是ConfirmKey
* KEYCODE_DPAD_CENTER
* KEYCODE_ENTER
* KEYCODE_SPACE
* KEYCODE_NUMPAD_ENTER

若按下的是ConfirmKey,且View是ENABLE和clickable的,则把View的状态设为Pressed。当然了View的状态改变,外观也会相应的改变(如果有设置的话),我们经常用和selector就是基于View的状态改变其外观的。最后还会进行一次LongClick检查,关于LongClick这块还不是很清楚。

public boolean onKeyDown(int keyCode, KeyEvent event) {    if (KeyEvent.isConfirmKey(keyCode)) {        if ((mViewFlags & ENABLED_MASK) == DISABLED) {            return true;        }        if (event.getRepeatCount() == 0) {            // Long clickable items don't necessarily have to be clickable.            final boolean clickable = (mViewFlags & CLICKABLE) == CLICKABLE                    || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;            if (clickable || (mViewFlags & TOOLTIP) == TOOLTIP) {                // For the purposes of menu anchoring and drawable hotspots,                // key events are considered to be at the center of the view.                final float x = getWidth() / 2f;                final float y = getHeight() / 2f;                if (clickable) {                    //设置View的状态为Pressed                    setPressed(true, x, y);                }                //LongClick这块还不太清楚                checkForLongClick(0, x, y);                return true;            }        }    }    return false;}

View的onKeyUp函数是这样实现的:首先确定按下的键是不是ConfirmKey,当KeyCode的值为以下的任一时,被认为是ConfirmKey
* KEYCODE_DPAD_CENTER
* KEYCODE_ENTER
* KEYCODE_SPACE
* KEYCODE_NUMPAD_ENTER

若按下的是ConfirmKey,且View是ENABLE和clickable的,并且View的状态为Pressed,并且key down的时长还不足以触发LongPress,就会调用View的performClick方法,该方法会回调OnClickListener的onClick方法,这个方法已经熟悉的不能再熟悉了。可以这样理解,当CondirmKey key down -> key up时会在当前focus的View上产生ClickEvent,然后View的OnClickListener会去处理这个Event。当然这是个想象出来的Event,因为程序其实并没有创建相应的ClickEvent对象。

public boolean onKeyUp(int keyCode, KeyEvent event) {    if (KeyEvent.isConfirmKey(keyCode)) {        if ((mViewFlags & ENABLED_MASK) == DISABLED) {            return true;        }        if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {            setPressed(false);            if (!mHasPerformedLongPress) {                // This is a tap, so remove the longpress check                removeLongPressCallback();                if (!event.isCanceled()) {                    //performClick中会回调OnClickListener的onClick方法                    return performClick();                }            }        }    }    return false;}

继续回到dispatchKeyEvent中,当KeyEvent最终都没有被消费掉的时候,会先调用InputEventConsistencyVerifier的onUnhandledEvent方法,处理下该event,具体干了啥,还不清楚。最后,dispatchKeyEvent返回false表示该Event还没有被处理。

if (mInputEventConsistencyVerifier != null) {    //onUnhandledEvent干了个啥,还不清楚    mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);}//dispatchKeyEvent在Event没有被处理的情况下返回falsereturn false;

总结

dispatch有派遣发送的意思,dispatchKeyEvent所做的事实际上是告诉View上注册过的Listener们,有个KeyEvent来了,请相关责任人(如OnKeyListener,OnClickListener等)出来处理一下吧。

原创粉丝点击