一、前言
Android 系统的智能电视,最近两年基本上越来越火了,而在现在手机 App 开发中,都是使用触摸的模式进行操作,但是在开发智能电视 App 的时候,View 的焦点寻址,就是我们绕不过去的坎。
那么,本文就 View 如何控制 Focus,来分析它背后的实现逻辑。
二、View 的焦点控制
在 Android 中,是有一套自己的焦点查找的算法,简单来说,就近原则,就是按方向就近查找下一个符合条件的 View。
如果我们有对一个 View 焦点控制的需求,需要对这个 View 强制指定它上下左右之后的下一个获取焦点的 View。可以通过 View 的属性来控制,只需要在对应方向上设定我们需要焦点转移的下一个 View 的 ID 即可。
- android:nextFocusDown:按下的时候,焦点寻址的 ViewId。
- android:nextFocusUp:按上的时候,焦点寻址的 ViewId。
- android:nextFocusLeft:按左的时候,焦点寻址的 ViewId。
- android:nextFocusRight:按右的时候,焦点寻址的 ViewId。
- android:nextFocusForward:向前的时候,焦点寻址的 ViewId。
这些都是最基本的,不是本文的主题。那么如果没有设定这些属性,而 Android 对 View 的 就近原则 的焦点查找算法,到底是如何实现的呢?
三、View 的焦点寻址
Android 中,是如何做到对 View 焦点的控制呢?
我们就先从 View 的源码看起,看看它是如何找到“下一个”位置的 View的,在 View 中,查找下一个应该获取焦点的 View ,使用的方法是 focusSearch()。
它需要传递一个 direction 参数,这个 direction 就是指定获取什么方向上的下一个位置的焦点。
而 View 并不会处理焦点寻址的具体逻辑,而是将焦点的查找委托给 mParent 来实现,mParent 是一个接口,它的实现类是 ViewRootImpl。
ViewRootImlp.focusSearch() 最终又将焦点寻址的任务,交托给 FocusFinder 来处理。
findNextFocus()中可以看到,findNextUserSpecifiedFocus() 方法正是用于查找我们对 View 设定不同方向的下一个焦点的 ViewId ,它的优先级是***的,如果没有找到,才会进行 findNextFocus() 通过算法来查找对应的 View。
为了证实这个说法,我们先看看 findNextUserSpecifiedFocus()的源码。
最终,又调回到 View.findUserSetNextFocus() 方法去寻找。
到这里也证实了我们的猜测,确实是通过 direction 来进行 View 的焦点寻址。
再回过头来看看 findNextFocus() 方法,如果通过 findNextUserSpecifiedFocus() 方法没有找到我们指定的 View,就会继续向下执行。这里声明的一个局部变量 focusables 就是用于存放符合算法的所有 View 。
接下来再看看如何向 focusables 这个 List 中,添加符合寻址要求的 View。在 findNextFocus() 中可以看到,它最终会调用 findNextFocusInAbsoluteDirection() 方法。
从 findNextFocusInAbsoluteDirection() 方法可以看出,就近原则就是在这里实现的,通过 View 的坐标点,计算出***要求的 View ,最终将找到的 View 返回过去。
到这里,基本就追踪到 View 对焦点寻址的完整逻辑。***补一个方法调用的流程图。
四、总结
如果有对 View 焦点的控制,可以考虑通过设置 View 的属性,还可以通过重写 View.focusSearch() 方法,来定制 View 焦点的寻址规则。不过一般而言,不推荐重写 focusSearch() 方法,只使用属性控制也能满足我们的需求。
【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】