「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」
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都会拦截事件。
今天的文章KeyEvent的分发流程分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15658.html