AndroidView焦点大部分与Android焦点相关的逻辑都在View、ViewGroup和FocusFinder这三个类中。ViewRootView对象有一个mParent变量(在添加到ViewGroup之后),它引用它的父容器。大多数View的mParent都是ViewGroup类型,除了根节点。一个Window中DecorView根节点的mParent称为ViewRoot。Android4.0之后,ViewRoot对应ViewRootImpl。它不是View的子类,而是一个ViewParent。ViewRootImpl是Window和DecorView之间的纽带。焦点、按钮、布局、渲染等过程都是从ViewRoot开始的。Viewfocus的基本流程如下。View(包括ViewGroup)通过以下三种方式获取焦点。View.java从上面可以看出前两个方法最终会执行到第三个方法。***的requestFocusNoSearch首先判断是否可以获取到焦点,然后进入下面的***流程:上面View.java的流程比较简单:如果没有焦点,先设置焦点标志,然后通知parent,然后刷新图片。主要流程在mParent的requestChildFocus中,后面会分析。在那里,焦点View会被逐层修改,原来的焦点View的焦点会被清除。onFocusChange会触发invalidate刷新,然后调用onFocusChangeListener。默认情况下,每个View只能设置一个onFocusChangeListener,而我们在开发中经常会遇到需要设置多个Listener的情况,我们可以重写onFocusChange方法来实现需要回调多个onFocusChangeListener。ViewGroup的焦点ViewGroup在View获取焦点的过程中获取焦点上面ViewGroup.java代码中的descendantFocusability决定是先按照View焦点流程处理(自己处理焦点)还是先处理FOCUS_BLOCK_DESCENDANTS给sub-看法。子View不允许获取焦点,则按照View流程执行FOCUS_BEFORE_DESCENDANTS按照Viewfirst的流程进行处理。如果自己拿不到焦点,那就给子进程处理FOCUS_AFTER_DESCENDANTS。尝试先让孩子集中注意力。如果获取不到焦点,那就按照View流程获取焦点。默认值为FOCUS_BEFORE_DESCENDANTS。我们可以通过setDescendantFocusability(intd)设置onRequestFocusInDescendants方法,为子类重写它,可以控制子View处理焦点。默认按照子View的顺序进行处理,向下或向右时从第一个开始,向上或向左时从最后一个开始,直到一个子View获取焦点注意,该方法只执行在本ViewGroup及其上层View调用requestFocus时获取父容器焦点的过程。在View获取焦点的过程中,会调用mParent.requestChildFocus来保持唯一的焦点在View树上。ViewGroup中保存焦点的子ViewViewGroup.java先清除自己的焦点。如果里面有焦点,先清除它的焦点,保存得到焦点的child,再调用上层的requestChildFocus。来自***的调用表明,这个方法会一直调用到View树的根节点。在当前ViewGroup中,任何获得焦点的child都会执行这个方法,所以这个方法也是ViewGroup知道child焦点变化的方法之一。(不幸的是,你无法知道孩子失去了焦点)Losingfocusorclearingfocus获取焦点可以是主动的,但是失去焦点一般是被动的(见上面代码),所以逻辑比较简单,只要获得焦点状态被清除。ViewGroup.javaView.java注意上面的方法是默认的包访问级别,我们不能重写或者调用,也可以主动清除焦点,这个过程和ViewGroup.javaView.javaViewGroup.java获取焦点的过程类似。以上就是AndroidView系统处理焦点所涉及的整个流程和方法,ViewRootImpl的requestChildFocus我们不需要关注clearChildFocus的实现。另外还有一些辅助方法booleanisFocusable()View是否能获得焦点booleanisFocused()View是否获得焦点booleanhasFocus()View/ViewGroupView内部是否有焦点findFocus()获取View/ViewGroup内部的焦点ViewViewgetFocusedChild()获取ViewGroup内部的焦点子ViewViewgetRootView()获取根节点View(一般为DecorView或toplayer代码中除了控制焦点外,系统会根据焦点移动自动处理一些方向键等未处理的按钮,ViewRootImpl.java代码对比见下面代码,但主要做三步如果View不处理button,将上下左右tabs等按钮转换成对应的方向,使用focusSearch方法在当前焦点View上寻找对应方向的下一个View,找到的View调用requestFocus。所以主要流程在focusSearch,View.java普通的View搜索什么都不做,交给parent来完成,ViewGroup.javaViewRootImpl我们可以重写focusSearch来控制e焦点移动顺序,默认的焦点移动顺序由FocusFinder决定。FocusFinder是一个公共工具类。FocusFinder是一个公共工具类。主要有两种方法可以在给定的View中找到指定View的下一个焦点或者指定方向的坐标如下:核心逻辑是两步,首先寻找setNextFocusXXId设置的View,如果不是按照最近的算法搜索,具体算法就不分析了,SDK里面有源码。总结上面的流程分析,当我们实现一个自定义View的时候,关注点的特殊需求有以下思路。requestFocus和clearFocus直接清除或转移焦点到View。除了onFocusChangeListener,你还可以在onFocusChange方法中,当某个View失去/获得焦点时通知ViewGroup。如果您只需要在子View获得焦点时得到通知。有一个requestChildFocus方法。重写onRequestFocusInDescendants方法可以在某些场景下控制ViewGroup的焦点,从而控制焦点的移动。您可以重写focusSearch方法。还有FocusFinder工具和上面的辅助方法。
