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
脱离窗口时调用。