转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52180375
Invalidate,requestLayout,requestFocus最终都会调用到ViewRoot中的schedulTraversale(),该函数发起一个异步消息,消息处理中调用performTraversals()方法对整个View进行遍历。
一个Window中View根节点DecorView, 它的mParent称为ViewRoot, 对应ViewRootImpl类, 它不是View的子类, 而是个ViewParent. ViewRootImpl是连接Window和DecorView的纽带, View的焦点, 按键, 布局, 渲染等流程都是从ViewRoot中开始的.
View绘制流程从requestLayout触发, View系统中所有会改变布局的方法都会触发requestLayout, 如TextView改变文字, ViewGroup添加View等.
View.java
public void requestLayout() {
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
View的requestLayout最终调用到ViewRootImpl
ViewRootImpl.java
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
从scheduleTraversals名字来看, requestLayout只是触发一个异步的任务. 事实上, View真正的绘制流程是从ViewRootImpl的performTraversals方法开始, 里面会经过measure, layout和draw三个过程. 其中measure用来测量View的宽高, layout用来确定View的位置, 而draw负责渲染View到屏幕上. 大致流程如下:
performTraversals会依次调用performMeasure, performlayout和performDraw方法. 父容器measure方法会调用onMeasure, onMeasure方法会对所有子元素进行measure过程, 以此遍历完整个View树. layout和draw流程类似.
measure过程计算View的宽高, 先看measure方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec)
参数measureSpec是父容器传的对View的尺寸规格限制. 根节点DecorView的measureSpec在ViewRoot中计算出.
MeasureSpec字面上可以理解为尺寸规格, 测量过程中父容器会根据自己的MeasureSpec和子View的LayoutParams转换成对应的MeasureSpec, 子View根据这个MeasureSpec来计算出宽高, 因此我理解MeasureSpec是父容器对子元素的尺寸限制, 这样对下面的源码就好理解
View.java
SpecMode有三种
上面提到子View的MeasureSpec是根据LayoutParams和父容器的MeasureSpec转换来的, 虽然我们可以自己写转换算法, 但是系统里面已经提供了完善的算法. 除了DecorView的MeasureSpec是ViewRootImpl构造出来的, 其他View的转换方法都一样.
ViewGroup.java
上述方法就是对子元素进行measure的, 在measure之前通过getChildMeasureSpec方法得到子元素的MeasureSpec.
ViewGroup.java
上面的方法就是根据父容器的MeasureSpec结合View的LayoutParams转换子元素的MeasureSpec
方法中三个参数意义
根节点View的MeasureSpec在performTraversals中得出. 先计算出窗口的最大可能尺寸desiredWindowWidth/desiredWindowHeight, 然后调用measureHierarchy方法来进入measure流程.:
ViewRootImpl.java
再看getRootMeasureSpec方法实现:
ViewRootImpl.java
DecorView的MeasureSpec根据窗口的LayoutParams按照如下规则生成
至此,view的工作原理到此结束了,接下来就是view的布局流程。见下篇
第一时间获得博客更新提醒,以及更多Android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码,即可关注。