前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android TV焦点总结

Android TV焦点总结

原创
作者头像
木子杂志
修改2020-08-12 10:06:34
2K0
修改2020-08-12 10:06:34
举报
文章被收录于专栏:木子杂志木子杂志
【1】 ViewRootImpl.ViewPostImeInputStage.onProcess()

当你拿着遥控器瞎按的时候,按键处理的起点是这儿

代码语言:txt
复制
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;
? ? ? } ?
}
【1.1】DecorView.dispatchKeyEvent()

【/frameworks/base/core/java/android/internal/policy/DecorView.java】

从此开始,DecorView开始进行KeyEvent的分发

代码语言:txt
复制
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);
? }
? ?
}
【1.2】Activity.dispatchKeyEvent()

按键分发来到Activity中

代码语言:txt
复制
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);
? } ? ?
? ?
}
【1.3】PhoneWindow

按键分发来到PhoneWindow中

兜兜转换 执行DecorView的

mDecor.superDispatchKeyEvent(event)方法,

又回到DecorView中

代码语言:txt
复制
public?class?PhoneWindow?extends?Window?implements?MenuBuilder.Callback?{

? ?//
? ?@Override
? ?public?boolean?superDispatchKeyEvent(KeyEvent?event) {
? ? ? ?//mDecor就是DecorView
? ? ? ?//
? ? ? ?return?mDecor.superDispatchKeyEvent(event);
? } ? ?
}
【1.4】

按键分发来到DecorView中

经过PhoneWindow的按键分发,又来到DecorView的superDispatchKeyEvent()方法

代码语言:txt
复制
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);
? } ? ?
}
【1.5】

按键分发来到ViewGroup中

至此我们按键的分发:

DecorView

【---->】

Activity

【---->】

DecorView

【---->】

ViewGroup

代码语言:txt
复制
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之间流转

【1.6】 View
代码语言:txt
复制
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,往上抛出

如果以上没有处理

执行自动寻焦的逻辑

【2】 ViewRootImpl.ViewPostImeInputStage.processKeyEvent()

按键事件自动寻焦

代码回到ViewRootImpl【1.1】返回 false ,代码继续往下执行

代码语言:txt
复制
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;
? ? ? } ?
? ? ? ?
? ?
}
【2.2】 performFocusNavigation()
代码语言:txt
复制
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;
        }
}
【2.3】View.focusSearch()
代码语言:txt
复制
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)

【2.4】FocusFinder
代码语言:txt
复制
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 {
            //...
        }
    }
}
  • 优先找开发者指定的下一个focus的视图 ,就是在xml或者代码中指定NextFocusDirection Id的视图
  • 其次,根据算法去找,原理就是找在方向上最近的视图

更多内容 欢迎关注公众号

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【1】 ViewRootImpl.ViewPostImeInputStage.onProcess()
  • 【1.1】DecorView.dispatchKeyEvent()
  • 【1.2】Activity.dispatchKeyEvent()
  • 【1.3】PhoneWindow
  • 【1.4】
  • 【1.5】
  • 【1.6】 View
  • 【2】 ViewRootImpl.ViewPostImeInputStage.processKeyEvent()
  • 【2.2】 performFocusNavigation()
  • 【2.3】View.focusSearch()
  • 【2.4】FocusFinder
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com