天天看点

[Android]Android焦点流程代码分析

以下内容为原创,欢迎转载,转载请注明

通过View的<code>View::focusSearch</code>进行焦点搜索对应方向上的下一个可以获取焦点的View:

不断地调用父控件来进行搜索,focusSearch有两个实现:<code>ViewGroup</code>和<code>RecyclerView</code>,先看<code>ViewGroup</code>:

如果是最顶层,则直接调用<code>FocusFinder::findNextFocus</code>方法进行搜索;否则调用父控件的<code>focusSearch</code>。<code>FocusFinder::findNextFocus</code>如下:

上面的<code>root</code>参数代表的是最顶层的view。

首先,通过尝试通过<code>findNextUserSpecifiedFocus</code>来查找下一个“指定的”可获得焦点的View,这个指定是开发者通过SDK自带的<code>setNextFocusLeftId</code>等方法进行手动设置的。如果查找到指定的下一个可获得焦点的View,则返回该View;否则,执行<code>View::addFocusables</code>方法,通过这个最顶层的View去拿到所有直接或间接的<code>Focusable</code>的子View,并添加到<code>ArrayList&lt;View&gt; focusables</code>中。

<code>View::addFolcusables</code>方法中有4种实现:

第一种是View中默认实现:

如果自己是focusable的话,直接把自己添加进去。

第二种是<code>ViewGroup</code>实现:

先会处理自身ViewGroup与它后代的关系(descendantFocusability),前面提到过,可能的几种情况: FOCUS_BEFORE_DESCENDANTS: ViewGroup本身先对焦点进行处理,如果没有处理则分发给child View进行处理 FOCUS_AFTER_DESCENDANTS: 先分发给Child View进行处理,如果所有的Child View都没有处理,则自己再处理 FOCUS_BLOCK_DESCENDANTS: ViewGroup本身进行处理,不管是否处理成功,都不会分发给ChildView进行处理

所以,以上:

如果不是<code>FOCUS_BLOCK_DESCENDANTS</code>,则首先检查<code>blockForTouchscreen</code>并重置掉<code>focusableMode</code>,然后遍历所有的子View,调用<code>child::addFocusables</code>。

如果不是<code>FOCUS_AFTER_DESCENDANTS</code>或者没有focusable的子View时自己处理,所以把自己加入到<code>views</code>中。

第三种是<code>ViewPager</code>实现:

与<code>ViewGroup</code>基本一致,在<code>descendantFocusability</code>不是<code>FOCUS_BLOCK_DESCENDANTS</code>时,遍历子View时判断view是否属于当前的page,如果是才加进去。如果不是<code>FOCUS_AFTER_DESCENDANTS</code>或者没有focusable的子View时自己处理,所以把自己加入到<code>views</code>中。

第四种是<code>RecyclerView</code>实现:

通过<code>LayoutManager::onAddFocusables</code>来进行管理,如果返回false,则直接调用父类<code>ViewGroup</code>的方法,看下<code>LayoutManager::onAddFocusables</code>的实现:

直接返回false,并且没有看到相关的<code>LayoutManager</code>对该方法的重写,所以,这里应该是直接调用了父类<code>ViewGroup</code>的方法。

所以重点是<code>FocusFinder::findNextFocus</code>方法的实现:

如果focused不是null,说明当前获取到焦点的View存在,则获得绘制焦点的Rect到focusedRect,然后根据rootView遍历所有ParentView从子View纠正坐标到根View坐标。

如果focused是null,则说明当前没有View获取到焦点,则把focusedRect根据不同的direction重置为“一点”。

最后根据direction调用<code>FocusFinder::findNextFocusInAbsoluteDirection</code>方法进行对比查找“下一个”View。

首先把最优选择mBestCandidateRect设置为focusedRect,根据方向做1像素的偏移便于对比。遍历所有刚刚查询出来的<code>focusables</code>,拿到每一个的<code>focusedRect</code>区域并进行转换,然后通过<code>FocusFinder::isBetterCandidate</code>方法进行对比,然后拿到更好的,遍历完成后就是最优选择。接下来看下<code>FocusFinder::isBetterCandidate</code>方法来了解下是怎么做对比的:

下面代码意思是:以source这个rect来说,作为对应derection上下一个focus view,rect1是否比rect2更优?

首先确定rect1是否<code>isCandidate</code>?<code>isCandidate</code>做的逻辑简单来说就是确定rect是否满足给定的derection作为下一个focus view这个条件,它的判断依据如下:

如果rect1不满足基本条件,则肯定返回false(基本的条件都不满足)

如果rect2不满足基本条件,则返回true,认为rect1更优

如果都满足基本条件的情况下,通过<code>FocusFinder::beamBeats</code>方法来判断哪种更优

接下来看下<code>FocusFinder::beamBeats</code>的实现:

首先通过<code>beamsOverlap</code>方法来判断两个rect与source是否重叠等等。注意的是,在水平情况下,如果rect1重叠,则就是最优解(为什么?比较奇怪),最后如果是竖直情况,通过<code>FocusFinder::majorAxisDistance</code>方法来判断哪个离source最近。如果还是比较不出,则通过<code>getWeightedDistanceFor</code>方法来通过“主要距离”和“次要距离”做一个综合的比较。

首先通过<code>onInterceptFocusSearch</code>进行拦截,如果返回具体的focus View,则直接返回;否则继续往下;<code>onInterceptFocusSearch</code>实现如下:

默认为空实现,返回null,也没有其它的子类进行重写,所以暂时不管这个处理,继续看<code>focusSearch</code>:

我们暂时只考虑<code>direction</code>为left,top,right,down的情况,则进入最外面if的else分支:

本文转自天天_byconan博客园博客,原文链接:http://www.cnblogs.com/tiantianbyconan/p/7286503.html,如需转载请自行联系原作者

继续阅读