Android View焦点总结

移动开发 Android
Android焦点相关逻辑大部分都在都在View, ViewGroup和FocusFinder三个类中。View对象都有一个mParent变量(添加到ViewGroup后), 代指其父容器. 绝大部分View的mParent都是ViewGroup类型, 除了根节点。一个Window中View根节点DecorView的mParent称为ViewRoot, 在安卓4.0后ViewRoot对应ViewRootImpl, 它不是View的子类, 而是个ViewParent。

Android View焦点

Android焦点相关逻辑大部分都在都在View, ViewGroup和FocusFinder三个类中.

ViewRoot

View对象都有一个mParent变量(添加到ViewGroup后), 代指其父容器. 绝大部分View的mParent都是ViewGroup类型, 除了根节点. 一个Window中View根节点DecorView的mParent称为ViewRoot, 在安卓4.0后ViewRoot对应ViewRootImpl, 它不是View的子类, 而是个ViewParent. ViewRootImpl是连接Window和DecorView的纽带, View的焦点, 按键, 布局, 渲染等流程都是从ViewRoot中开始的.

View的焦点

基本流程如下

 

 

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焦点流程处理(自己处理焦点)还是先把给子View处理

FOCUS_BLOCK_DESCENDANTS 不允许子View获取焦点, 那么按照View的流程进行

FOCUS_BEFORE_DESCENDANTS 先按照View的流程处理, 如果自己不能获取焦点则给孩子处理

FOCUS_AFTER_DESCENDANTS 先尝试给孩子焦点, 如果没有可获取焦点再按照View流程自己获取焦点

默认值FOCUS_BEFORE_DESCENDANTS, 我们可以通过setDescendantFocusability(int d)

设置

onRequestFocusInDescendants方法是给子类重写使用, 可以控制子View处理焦点. 默认按照子View顺序处理, direction向下或向右则从***个开始, 向上或向左则从***一个开始, 直到某个子View获取焦点

注意此方法只在此ViewGroup及其上层View上调用requestFocus时会执行到

父容器焦点的处理

在View获取焦点流程中会调用mParent.requestChildFocus, 维护View树上焦点唯一, 在各层ViewGroup中保存有焦点的子View

ViewGroup.java 

 

 

 

先清除自己的焦点, 如果原来内部有焦点, 先清除其焦点, 保存获取焦点的孩子, 然后调用上一层的requestChildFocus. ***的调用可知, 这个方法会一直调用到View的树的root节点.

在当前ViewGroup内部, 任何一个孩子取得焦点都会执行到这个方法, 因此此方法也是ViewGroup得知孩子焦点变化的方法之一.(可惜不能得知孩子失去焦点)

失去焦点或清除焦点

获取焦点可以是主动的, 但失去焦点一般都是被动的(见上面的代码), 因此逻辑相对简单, 只要清除焦点状态即可.

ViewGroup.java 

 

 

 

View.java

 

 

注意上面的方法是默认package访问级别的, 我们无法重写也不能调用

也可以主动清除焦点, 与获取焦点流程相似

ViewGroup.java 

 

 

 

View.java 

 

 

 

ViewGroup.java

以上是安卓View系统焦点处理的全部流程和涉及到的方法, ViewRootImpl的requestChildFocus和clearChildFocus实现我们不需要关注

另外还有以下一些辅助方法

boolean isFocusable() View是否可以获取焦点

boolean isFocused() View是否获取焦点

boolean hasFocus() View/ViewGroup内部是否有焦点

View findFocus() 取到View/ViewGroup内部的焦点View

View getFocusedChild() 取到ViewGroup内部有焦点的子View

View getRootView() 取到根节点View(一般是DecorView或顶层ViewGroup)

焦点移动

除了在代码里面控制焦点, 系统对没有处理的方向键等一些按键自动按照焦点移动来处理, 见下面代码

ViewRootImpl.java 

 

 

 

 

 

 

代码比较上, 但是主要做了三个步骤

如果View没有处理按键, 把上下左右tab等按键转换成对应方向

在当前焦点View上通过focusSearch方法查找对应方向的下一个View

查找到的View调用requestFocus因此主要的流程在focusSearch中

View.java 

 

 

 

普通View查找什么都没做, 交给parent来完成.

ViewGroup.java 

 

 

 

ViewRootImpl 

 

 

 

 

我们可以重写focusSearch控制焦点移动顺序, 而默认的焦点移动顺序由FocusFinder决定

FocusFinder查找焦点

FocusFinder为public的工具类, 主要就两个方法, 可以在给定的View内在指定方向查找指定View或坐标的下一个焦点如下: 

 

 

 

核心逻辑就两步, 先查找setNextFocusXXId设置的View, 如果没有按照就近算法查找.具体算法不再分析, SDK里面有源码.

总结

综合上面的流程分析, 我们在实现自定义View时, 对焦点的特殊需求有如下思路

requestFocus和clearFocus直接对View清除或转移焦点

除了onFocusChangeListener, 还可以在onFocusChange方法中实现一些View失去/获得焦点时通知

对ViewGroup, 如果只需要在子View获取焦点时得到通知, 有requestChildFocus方法.

重写onRequestFocusInDescendants方法可以控制某些情景下ViewGroup焦点

控制焦点移动可以重写focusSearch方法

另外还有FocusFinder工具和上面的辅助方法.

责任编辑:庞桂玉 来源: Android开发中文站
相关推荐

2017-12-28 14:51:01

AndroidView焦点

2016-03-14 09:43:47

androidview总结

2013-07-16 14:47:18

Android EdiEditText不弹出Android开发

2011-01-26 10:21:30

view类Qt

2017-03-14 15:09:18

AndroidView圆形进度条

2011-12-23 10:17:25

Android音乐编程管理音频焦点

2016-11-16 21:55:55

源码分析自定义view androi

2016-12-26 15:25:59

Android自定义View

2011-11-04 16:34:20

控件Android

2016-04-12 10:07:55

AndroidViewList

2017-03-07 13:03:34

AndroidView知识问答

2014-07-29 15:57:01

ContentProv

2010-01-27 16:41:48

Android特点

2010-01-26 17:05:37

Android缺点

2010-01-27 17:45:15

Android应用技巧

2017-08-21 21:36:23

AndroidViewJava

2016-12-12 14:55:01

AndroidAndroid Vie

2011-02-22 11:11:33

EditTextAndroid

2010-03-05 13:40:29

Android Vie

2016-12-28 10:23:33

Android适配难题
点赞
收藏

51CTO技术栈公众号