标签:
请尊重分享成果,转载请注明出处:
http://blog.csdn.net/hejjunlin/article/details/52263256
前言:View框架写到第六篇,发现前面第二篇竟然没有,然后事情是在微信公众号发了,忘记在博客上更新,所以关注微信公众号的应该都看过了,趁今天有时间遂补上。(PS:本篇文章中源码均是android 6.0,请知晓)
本来之前说view下篇是写onMeasure,onLayou,onDraw相关的,笔者做盒子开发,遥控器按键,碰到的都是焦点控制相关。所以先把焦点放到了onMeasure,onLayou,onDraw之前。以前踩过不少坑,最近公司同事做分享,遂下决心总结下,Agenda如下:
Android焦点相关逻辑大部分都在都在View, ViewGroup和FocusFinder三个类中.
View对象都有一个mParent变量(添加到ViewGroup后), 代指其父容器. 绝大部分View的mParent都是ViewGroup类型, 除了根节点. 一个Window中View根节点DecorView的mParent称为ViewRoot, 在安卓4.0后ViewRoot对应ViewRootImpl, 它不是View的子类, 而是个ViewParent. ViewRootImpl是连接Window和DecorView的纽带, View的焦点, 按键, 布局, 渲染等流程都是从ViewRoot中开始的.
基本流程如下
View(包括ViewGroup)获取焦点都通过如下三个方法:
View.java -> requestFocus()
从上面可以看到前两个最终会执行到第三个方法.
最后的requestFocusNoSearch先判断是否可以获取焦点, 然后进入下面的流程:
View.java -> requestFocusNoSearch()
View.java -> handleFocusGainInternal()
上面的流程比较简单: 如果当前没有焦点, 先置焦点标志, 再通知parent, 然后刷新图片.
主要的流程在mParent的requestChildFocus里面, 后面会分析. 那里会逐层向上修改焦点View并清除原来有焦点的View的焦点
onFocusChange会触发invalidate刷新, 然后调用onFocusChangeListener. 默认情况每个View只能设置一个onFocusChangeListener, 而开发中经常遇到需要设置多个Listener的情况, 我们就可以重写onFocusChange方法, 实现回调多个onFocusChangeListener的需求.
ViewGroup获取焦点是在View获取焦点流程中多了内部焦点处理
ViewGroup.java -> requestFocus()
上面代码中descendantFocusability决定了是先按View焦点流程处理(自己处理焦点)还是先把给子View处理
onRequestFocusInDescendants方法是给子类重写使用, 可以控制子View处理焦点. 默认按照子View顺序处理, direction向下或向右则从第一个开始, 向上或向左则从最后一个开始, 直到某个子View获取焦点
(注意此方法只在此ViewGroup及其上层View上调用requestFocus时会执行到)
在View获取焦点流程中会调用mParent.requestChildFocus, 维护View树上焦点唯一, 在各层ViewGroup中保存有焦点的子View
ViewGroup.java -> requestChildFocus()
先清除自己的焦点, 如果原来内部有焦点, 先清除其焦点, 保存获取焦点的孩子, 然后调用上一层的requestChildFocus. 最后的调用可知, 这个方法会一直调用到View的树的root节点.
在当前ViewGroup内部, 任何一个孩子取得焦点都会执行到这个方法, 因此此方法也是ViewGroup得知孩子焦点变化的方法之一.(可惜不能得知孩子失去焦点)
获取焦点可以是主动的, 但失去焦点一般都是被动的(见上面的代码), 因此逻辑相对简单, 只要清除焦点状态即可.
ViewGroup.java -> unFocus()
View.java -> unFocus() -> clearFocusInternal()
注意上面的方法是默认package访问级别的, 我们无法重写也不能调用
也可以主动清除焦点, 与获取焦点流程相似
View.java -> clearFocus()
ViewGroup.java -> clearFocus()
ViewGroup.java -> clearChildFocus()
以上是安卓View系统焦点处理的全部流程和涉及到的方法, ViewRootImpl的requestChildFocus和clearChildFocus实现我们不需要关注
另外还有以下一些辅助方法
除了在代码里面控制焦点, 系统对没有处理的方向键等一些按键自动按照焦点移动来处理, 见下面代码
ViewRootImpl.java -> processKeyEvent()
代码比较上, 但是主要做了三个步骤
View.java -> focusSearch()
普通View查找什么都没做, 交给parent来完成.
ViewGroup.java -> focusSearch()
ViewRootImpl.java -> focusSearch()
我们可以重写focusSearch控制焦点移动顺序, 而默认的焦点移动顺序由FocusFinder决定
FocusFinder为public的工具类, 主要就两个方法, 可以在给定的View内在指定方向查找指定View或坐标的下一个焦点
如下:
FocusFinder.java -> findNextFocus()
核心逻辑就两步, 先查找setNextFocusXXId设置的View, 如果没有按照就近算法查找.
具体算法不再分析, SDK里面有源码.
综合上面的流程分析, 我们在实现自定义View时, 对焦点的特殊需求有如下思路
第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。
如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易
标签:
原文地址:http://blog.csdn.net/hejjunlin/article/details/52263256