KeyEvent的分发流程

KeyEvent的分发流程「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」 KeyEvent的分发流程 在Tv开发中使用 dispatchKeyEvent、onKeyDown/Up、onKeyLi

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

KeyEvent的分发流程

在Tv开发中使用 dispatchKeyEvent、onKeyDown/Up、onKeyLisenter 等来分发处理,不像触屏手机通过 dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent 来分发处理了。本次主要讲解View树内部的事件分发流程


当我们在点击遥控器按键时,会有ACTION_DOWNACTION_UP两个KeyEvent进行分发处理,他们分发的流程都是一样的。

1. PhoneWindow ( DecorView的dispatchKeyEvent )

当接受到KeyEvent事件后首先交给DecorView的dispatchKeyEvent方法进行事件的分发。

/frameworks/base/core/java/com/android/internal/policy/DecorView.java$dispatchKeyEvent

 if (!mWindow.isDestroyed()) {
     final Window.Callback cb = mWindow.getCallback();
     final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
         : super.dispatchKeyEvent(event);
     if (handled) {
         return true;
     }
 }

Activity的attach方法:

frameworks/base/core/java/android/app/Activity.java$attach

mWindow.setCallback(this);

Activity实现了Window.Callback接口,所以cb就代表着当前Activity。所以这里实际是调用的ActivitydispatchKeyEvent,交由Activity去分发处理。

2. Activity的的dispatchKeyEvent

frameworks/base/core/java/android/app/Activity.java$dispatchKeyEvent

Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
    return true;
}

Activity会通过getWindow()先获取PhoneWindow对象;调用PhoneWindowsuperDispatchKeyEvent方法。

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java$superDispatchKeyEvent

public boolean superDispatchKeyEvent(KeyEvent event) {
    return mDecor.superDispatchKeyEvent(event);
}

PhoneWindow不做其他处理,转而调用DecorViewsuperDispatchKeyEvent

3. ViewGroup的dispatchKeyEvent

/frameworks/base/core/java/com/android/internal/policy/DecorView.java$superDispatchKeyEvent

if (super.dispatchKeyEvent(event)) {
    return true;
}

DecorView则调用父类super.dispatchKeyEvent(event),DecorView继承ViewGroup,所以实际上是交给了ViewGroup的dispatchKeyEvent进行事件的分发。

/frameworks/base/core/java/android/view/ViewGroup.java$dispatchKeyEvent

public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 1);
        }
​
        if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
                == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
            if (super.dispatchKeyEvent(event)) {
                return true;
            }
        } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
                == PFLAG_HAS_BOUNDS) {
            if (mFocused.dispatchKeyEvent(event)) {
                return true;
            }
        }
​
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
        }
        return false;
    }

ViewGroup会递归寻找当前焦点的子View将事件传给子View的dispatchKeyEvent分发处理。

4. 处理流程

KeyEvent的处理有两个地方,分别是Activity和View。

ViewGroup中只负责分发。事件返回True表示事件被消耗。

View 的 dispatchKeyEvent

/frameworks/base/core/java/android/view/KeyEvent.java

当分发到View的dispatchKeyEvent时,首先会查看是否设置了Key的Listener监听,如果存在则回调OnKeyListener.onKey()的方法进行处理。

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

如果没有设置OnKeyListener或者OnKeyListener.onKey()返回为false的话View会调用KeyEvent的dispatch回调View的onKeyDown/Up时间去处理。

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

KeyEvent的dispatch

 boolean res = receiver.onKeyDown(mKeyCode, this);

因为是view调用的KeyEvent的dispatch,所以是通过KeyEvent回调view自己的KeyDown或KeyUp事件。Activity同理。

View的onKeyUp

如果没有重写View的onKeyUp方法,而且KeyEvent.isConfirmKey(keyCode)为true 确认按键 ACTION_UP

 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()) {
                     return performClickInternal();
                 }
             }
         }
     }
     return false;
    }

performClickInternal()会去检测是否有OnClickListener监听器,如果有的话会直接消费事件

这里强调一下是消费,如果有对 View 设置 OnClickListener 监听器的话,而且事件没有在上面被消费掉,一定会在 onClick() 中被消耗掉。View 在内部 dispatchKeyEvent() 里分发事件给 onClick 时已经默认返回 true 表示事件被消耗掉了。

如果在view中没有处理事件,也就是没有OnClickListener监听或者,KeyDown和KeyUp的返回值为false,事件将会返回到Activity调用他自己的KeyDown和KeyUp事件,将事件交由Activity处理。

Activity重写dispatchKeyEvent返回true或false都会拦截事件。

今天的文章KeyEvent的分发流程分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15658.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注