阅读 202

KeyEvent的分发流程(keyevent事件值)

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都会拦截事件。


作者:还是白嫖香
链接:https://juejin.cn/post/7029254929889886244


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐