当你拿着遥控器瞎按的时候,按键处理的起点是这儿
public?final?class?ViewRootImpl?implements?ViewParent,
? ? ? ?View.AttachInfo.Callbacks,?ThreadedRenderer.DrawCallbacks?{
? ?
? ?final?class?ViewPostImeInputStage?extends?InputStage?{
? ? ? ?@Override
? ? ? ?protected?int?onProcess(QueuedInputEvent?q) {
? ? ? ? ? ?if?(q.mEvent?instanceof?KeyEvent) {
? ? ? ? ? ? ? ?//按键处理的起点
? ? ? ? ? ? ? ?return?processKeyEvent(q);
? ? ? ? ? }?else?{
? ? ? ? ? ? ? ?//...
? ? ? ? ? }
? ? ? } ? ? ? ?
? }
? ?
? ? ? ?private?int?processKeyEvent(QueuedInputEvent?q) {
? ? ? ? ? ?final?KeyEvent?event?=?(KeyEvent)q.mEvent;
? ? ? ? ? ?//【1.1】
? ? ? ? ? ?//这里的 mView 当然是我们的 DecorView,
? ? ? ? ? ?if?(mView.dispatchKeyEvent(event)) {
? ? ? ? ? ? ? ?return?FINISH_HANDLED;
? ? ? ? ? }
? ? ? ? ? ?//...
? ? ? ? ? ?// Handle automatic focus changes.
? ? ? ? ? ?if?(event.getAction()?==?KeyEvent.ACTION_DOWN) {
? ? ? ? ? ? ? ?if?(groupNavigationDirection?!=?0) {
? ? ? ? ? ? ? ? ? ?//...
? ? ? ? ? ? ? }?else?{
? ? ? ? ? ? ? ? ? ?//关键代码
? ? ? ? ? ? ? ? ? ?// 自动寻焦的逻辑【2】
? ? ? ? ? ? ? ? ? ?if?(performFocusNavigation(event)) {
? ? ? ? ? ? ? ? ? ? ? ?return?FINISH_HANDLED;
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? ? ?return?FORWARD;
? ? ? } ?
}
【/frameworks/base/core/java/android/internal/policy/DecorView.java】
从此开始,DecorView开始进行KeyEvent的分发
public?class?DecorView?extends?FrameLayout?implements?RootViewSurfaceTaker,?WindowCallbacks?{
? ?
? ?
? ?//1. 重写了父类FrameLayout的dispatchKeyEvent()方法,但不会执行 super.dispatchKeyEvent()了
? ?//2.
? ?@Override
? ?public?boolean?dispatchKeyEvent(KeyEvent?event) {
? ? ? ?final?int?keyCode?=?event.getKeyCode();
? ? ? ?final?int?action?=?event.getAction();
? ? ? ?final?boolean?isDown?=?action?==?KeyEvent.ACTION_DOWN;
? ? ? ?//...
? ? ? ?if?(!mWindow.isDestroyed()) {
? ? ? ? ? ?// 获取 Callback对象 就是activity
? ? ? ? ? ?//见【1.2】
? ? ? ? ? ?final?Window.Callback?cb?=?mWindow.getCallback();
? ? ? ? ? ?//执行activity 的dispatchKeyEvent(),super.dispatchKeyEvent(event)不会执行滴
? ? ? ? ? ?final?boolean?handled?=?cb?!=?null?&&?mFeatureId?<?0???cb.dispatchKeyEvent(event)
? ? ? ? ? ? ? ? ? :?super.dispatchKeyEvent(event);
? ? ? ? ? ?if?(handled) {
? ? ? ? ? ? ? ?return?true;
? ? ? ? ? }
? ? ? }
? ? ? ?return?isDown???mWindow.onKeyDown(mFeatureId,?event.getKeyCode(),?event)
? ? ? ? ? ? ? :?mWindow.onKeyUp(mFeatureId,?event.getKeyCode(),?event);
? }
? ?
}
按键分发来到Activity中
public?class?Activity?extends?ContextThemeWrapper
? ? ? ?implements?LayoutInflater.Factory2,
? ? ? ?Window.Callback{
? ?
? ?//参数省略了..
? ?final?void?attach() {
? ? ? ?//这儿将 activity 传入
? ? ? ?mWindow?=?new?PhoneWindow(this,?window,?activityConfigCallback);
? ? ? ?mWindow.setWindowControllerCallback(this);
? ? ? ?//setCallback
? ? ? ?mWindow.setCallback(this);
? ? ? ?//...
? }
?
? ?// 注意 Activity实现了Window.Callback接口
? ?//dispatchKeyEvent()方法 就是来自Window.Callback
? ?public?boolean?dispatchKeyEvent(KeyEvent?event) {
? ? ? ?onUserInteraction();
? ? ? ?//...
? ? ? ?//win 是PhoneWindow 对象
? ? ? ?Window?win?=?getWindow();
? ? ? ?//关键代码【1.3】
? ? ? ?if?(win.superDispatchKeyEvent(event)) {
? ? ? ? ? ?return?true;
? ? ? }
? ? ? ?View?decor?=?mDecor;
? ? ? ?if?(decor?==?null)?decor?=?win.getDecorView();
? ? ? ?return?event.dispatch(this,?decor?!=?null
? ? ? ? ? ? ? ???decor.getKeyDispatcherState() :?null,?this);
? } ? ?
? ?
}
按键分发来到PhoneWindow中
兜兜转换 执行DecorView的
mDecor.superDispatchKeyEvent(event)方法,
又回到DecorView中
public?class?PhoneWindow?extends?Window?implements?MenuBuilder.Callback?{
? ?//
? ?@Override
? ?public?boolean?superDispatchKeyEvent(KeyEvent?event) {
? ? ? ?//mDecor就是DecorView
? ? ? ?//
? ? ? ?return?mDecor.superDispatchKeyEvent(event);
? } ? ?
}
按键分发来到DecorView中
经过PhoneWindow的按键分发,又来到DecorView的superDispatchKeyEvent()方法
public?class?DecorView?extends?FrameLayout?implements?RootViewSurfaceTaker,?WindowCallbacks?{
? ?
? ?public?boolean?superDispatchKeyEvent(KeyEvent?event) {
? ? ? ?//...
? ? ? ?//执行 父类的dispatchKeyEvent()方法
? ? ? ?//父类是FrameLayout,发现FrameLayout没有dispatchKeyEvent()
? ? ? ?//进入ViewGroup 【1.5】
? ? ? ?if?(super.dispatchKeyEvent(event)) {
? ? ? ? ? ?return?true;
? ? ? }
? ? ? ?return?(getViewRootImpl()?!=?null)?&&?getViewRootImpl().dispatchUnhandledKeyEvent(event);
? } ? ?
}
按键分发来到ViewGroup中
至此我们按键的分发:
DecorView
【---->】
Activity
【---->】
DecorView
【---->】
ViewGroup
public?abstract?class?ViewGroup?extends?View?implements?ViewParent,?ViewManager?{
? ?
? ?//
? ?@Override
? ?public?boolean?dispatchKeyEvent(KeyEvent?event) {
? ? ? ?if?(mInputEventConsistencyVerifier?!=?null) {
? ? ? ? ? ?mInputEventConsistencyVerifier.onKeyEvent(event,?1);
? ? ? }
? ? ? ?if?((mPrivateFlags?&?(PFLAG_FOCUSED?|?PFLAG_HAS_BOUNDS))
? ? ? ? ? ? ? ?==?(PFLAG_FOCUSED?|?PFLAG_HAS_BOUNDS)) {
? ? ? ? ? ?//如果 view 处理返回 true,这里也返回 true
? ? ? ? ? ?//【1.6】 View
? ? ? ? ? ?if?(super.dispatchKeyEvent(event)) {
? ? ? ? ? ? ? ?return?true;
? ? ? ? ? }
? ? ? }?else?if?(mFocused?!=?null?&&?(mFocused.mPrivateFlags?&?PFLAG_HAS_BOUNDS)
? ? ? ? ? ? ? ?==?PFLAG_HAS_BOUNDS) {
? ? ? ? ? ?//ViewGroup自己把KeyEvent交给mFocused处理 ?
? ? ? ? ? ?//ViewGroup处理 也是回调view 的 dispatchKeyEvent()
? ? ? ? ? ?if?(mFocused.dispatchKeyEvent(event)) {
? ? ? ? ? ? ? ?return?true;
? ? ? ? ? }
? ? ? }
? ? ? ?//...
? ? ? ?return?false;
? } ? ?
}
通过flag的判断,有两个处理路径,也可以看到在处理keyEvent时,ViewGroup扮演两个角色:
1.View的角色,也就是此时keyEvent需要在自己与其他View之间流转
2.ViewGroup的角色,此时keyEvent需要在自己的子View之间流转
public?class?View?implements?Drawable.Callback,?KeyEvent.Callback,
? ? ? ?AccessibilityEventSource?{
? ?
? ?public?boolean?dispatchKeyEvent(KeyEvent?event) {
? ? ? ??//如果设置了mOnKeyListener,则优先走onKey方法
? ? ? ?ListenerInfo?li?=?mListenerInfo;
? ? ? ?if?(li?!=?null?&&?li.mOnKeyListener?!=?null?&&?(mViewFlags?&?ENABLED_MASK)?==?ENABLED
? ? ? ? ? ? ? ?&&?li.mOnKeyListener.onKey(this,?event.getKeyCode(),?event)) {
? ? ? ? ? ?return?true;
? ? ? }
? ? ? ?//把View自己当作参数传入,调用KeyEvent的dispatch方法 在里面回调 onKeyDown/onKeyUp
? ? ? ?if?(event.dispatch(this,?mAttachInfo?!=?null
? ? ? ? ? ? ? ???mAttachInfo.mKeyDispatchState?:?null,?this)) {
? ? ? ? ? ?return?true;
? ? ? }
? ? ? ?//...
? ? ? ?return?false;
? } ? ?
}
View这里,会优先处理OnKeyListener的onKey回调。
然后才可能会走KeyEvent的dispatch,最终走到View的OnKeyDown或者OnKeyUp
ok,我要开始写绕口令了...
上面的执行流程总结就是:
DecorView.dispatchKeyEvent() 如果下面向上返回 false ?交给 window 处理
【---->】
?Activity.dispatchKeyEvent() ?如果下面向上返回 false 先交给自己的onKey???????? Down/onKeyUp处理 ,还是返回 false,往上抛出交给 DecorView 处理
【---->】
PhoneWindow.superDispatchKeyEvent() ?如果下面向上返回 false ,往上抛出交给 Activity 处理
【---->】
DecorView.superDispatchKeyEvent() 如果下面向上返回 false ,往上抛出交给 PhoneWindow 处理
【---->】
DecorView.this.super.dispatchKeyEvent(event) 如果下面向上返回 false ,往上抛出
【---->】
ViewGroup.dispatchKeyEvent() 如果下面向上返回 false ,看看自己或者子 view 有没有焦点,如果有,自身处理 先执行onKey(),
没有处理,再执行onKeyDown/onKeyUp ,还是返回 false,往上抛出
【---->】
View.dispatchKeyEvent 看看有没有焦点,如果有,自身处理 先执行onKey(),没有处理,再执行onKeyDown/onKeyUp ,还是返回 false,往上抛出
如果以上没有处理
执行自动寻焦的逻辑
按键事件自动寻焦
代码回到ViewRootImpl【1.1】返回 false ,代码继续往下执行
public?final?class?ViewRootImpl?implements?ViewParent,
? ? ? ?View.AttachInfo.Callbacks,?ThreadedRenderer.DrawCallbacks?{
? ?
? ?final?class?ViewPostImeInputStage?extends?InputStage?{
? ? ? ?@Override
? ? ? ?protected?int?onProcess(QueuedInputEvent?q) {
? ? ? ? ? ?if?(q.mEvent?instanceof?KeyEvent) {
? ? ? ? ? ? ? ?//按键处理的起点
? ? ? ? ? ? ? ?return?processKeyEvent(q);
? ? ? ? ? }?else?{
? ? ? ? ? ? ? ?//...
? ? ? ? ? }
? ? ? } ? ? ? ?
? }??
? ? ? ?private?int?processKeyEvent(QueuedInputEvent?q) {
? ? ? ? ? ?final?KeyEvent?event?=?(KeyEvent)q.mEvent;
? ? ? ? ? ?//【1.1】
? ? ? ? ? ?//这里的 mView 当然是我们的 DecorView,
? ? ? ? ? ?if?(mView.dispatchKeyEvent(event)) {
? ? ? ? ? ? ? ?return?FINISH_HANDLED;
? ? ? ? ? }
? ? ? ? ? ?//...
? ? ? ? ? ?// Handle automatic focus changes.
? ? ? ? ? ?if?(event.getAction()?==?KeyEvent.ACTION_DOWN) {
? ? ? ? ? ? ? ?if?(groupNavigationDirection?!=?0) {
? ? ? ? ? ? ? ? ? ?//...
? ? ? ? ? ? ? }?else?{
? ? ? ? ? ? ? ? ? ?//关键代码
? ? ? ? ? ? ? ? ? ?// 自动寻焦的逻辑【2.2】
? ? ? ? ? ? ? ? ? ?if?(performFocusNavigation(event)) {
? ? ? ? ? ? ? ? ? ? ? ?return?FINISH_HANDLED;
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? ? ?return?FORWARD;
? ? ? } ?
? ? ? ?
? ?
}
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private boolean performFocusNavigation(KeyEvent event) {
//...
if (direction != 0) {
View focused = mView.findFocus();
if (focused != null) {
//关键代码
//【2.3】
View v = focused.focusSearch(direction);
if (v != null && v != focused) {
//...
}
// Give the focused view a last chance to handle the dpad key.
if (mView.dispatchUnhandledMove(focused, direction)) {
return true;
}
} else {
if (mView.restoreDefaultFocus()) {
return true;
}
}
}
return false;
}
}
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public View focusSearch(@FocusRealDirection int direction) {
if (mParent != null) {
//关键代码
return mParent.focusSearch(this, direction);
} else {
return null; }}}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
//
public View focusSearch(View focused, int direction) {
//最终进入 if 分支
if (isRootNamespace()) {
//关键代码 【2.4】
return FocusFinder.getInstance().findNextFocus(this, focused, direction);
} else if (mParent != null) {
//不是 root 布局 继续递归,最终进入 if 分支
return mParent.focusSearch(focused, direction);
}
return null; }
}
View并不会直接去找,而是交给它的parent去找,
而 VieGroup 呢?来一个递归的判断直到是顶层布局,执行FocusFinder.getInstance().findNextFocus(this, focused, direction)
public class FocusFinder {
private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
View next = null;
ViewGroup effectiveRoot = getEffectiveRoot(root, focused);
if (focused != null) {
//优先从xml或者代码中指定focusid的View中找
next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
}
if (next != null) {
return next;
}
ArrayList<View> focusables = mTempList;
try {
focusables.clear();
effectiveRoot.addFocusables(focusables, direction);
if (!focusables.isEmpty()) {
//其次,根据算法去找,原理就是找在方向上最近的View
next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
}
} finally {
focusables.clear();
}
return next;
}
private View findNextFocus(ViewGroup root, View focused, Rect focusedRect,
int direction, ArrayList<View> focusables) {
if (focused != null) {
//...
} else {
//...
}
}
}
更多内容 欢迎关注公众号
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。