View的工作原理
三大流程
- measure 用来测量View的宽高;
- layout 用来确定View在父容器中的放置位置;
- draw 负责绘制。
在ActivityThread中,当Activity创建完毕后,会将DecorView附加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl与DecorView建立关联。
ViewRootIml其实是DecorView的管理类。
View的绘制流程是从ViewRoot的performTraversals()方法开始的,经过measure、layout、draw三个过程才能最终将一个View绘制出来。
measure
通过调用setMeasuredDimension决定View的宽高。
计算之后可以通过getMeasureWidth和getMeasureHeight获取测量后的结构。
measureChildren
通过遍历调用measureChild(child,widthMeasureSpec,heightMeasureSpec)方法完成所有子View的测量。
layout
用来确定子元素的位置。需要遍历子元素,调用其layout方法。
决定了View的四个顶点的坐标和实际的View的宽和高。
- getTop
- getLeft
- getRight
- getBottom
- getWidth
- getHeight
getWidth和getHeight才是View的最终宽高。
onDraw
- 绘制背景background.draw(canvas)。
- 绘制自己(onDraw)。
- 绘制children(dispatchDraw)。
- 绘制装饰(onDrawScrollBars)。
DecorView
DecorView作为顶级的View,包含一个竖直的LinnerLayout,内部含有标题栏和内容栏.setContentView就是把View附加到内容栏。内容栏是个FrameLayout,他的的id是content,所以是setContentView。
| |
MeasureSpec
这是一个32位的int,高2位代表SpecMode,低30位代表SpecSize。
通过将SpecMode和SpecSize打包成一个int,避免过多的内存分配。
- UNSPECIFIED 父容器不做任何闲置,子View可以任意大;
- EXACTLY 父容器测出View所需大小,对应match_parent和精确的值;
- AT_MOST 父容器制定了一个可用大小SpecSize,View的大小不能大于这个值,用于wrap_content。
获取View的宽高
view的measure过程和Activitu生命周期不同步,所有在onCreate、onStart、onResume中都不能获取到View的宽高。
- Activity/View#onWindowFocusChanged;
- view.post(runnable);
- ViewtreeObserver
- view.measure(int widthMeasureSpec,int heightMeasureSpec)(手动测量);
生命周期
Constructors构造函数;onFinishInflate当该View及其子View从XML文件中填充完成后会被调用;onAttachedToWindow附加到窗口;onMeasure计算尺寸时调用;onSizeChanged当前view尺寸变化时调用;onLayout调用所有子view的layout方法为每一个子view确定位置;onDraw绘图时调用;onDetackedFromWindow脱离窗口时调用。