KeyEvent的分发流程(keyevent事件值)
KeyEvent的分发流程
在Tv开发中使用 dispatchKeyEvent、onKeyDown/Up、onKeyLisenter 等来分发处理,不像触屏手机通过 dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent 来分发处理了。本次主要讲解View树内部的事件分发流程。
当我们在点击遥控器按键时,会有ACTION_DOWN和ACTION_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。所以这里实际是调用的Activity的dispatchKeyEvent,交由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对象;调用PhoneWindow的superDispatchKeyEvent方法。
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java$superDispatchKeyEvent
public boolean superDispatchKeyEvent(KeyEvent event) { return mDecor.superDispatchKeyEvent(event); } 复制代码
PhoneWindow不做其他处理,转而调用DecorView的superDispatchKeyEvent
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