作为一个Android开发人员,我们每天开发工作都会与View打交道,Android提供的任何布局,控件都是直接或者间接的继承View的,如LinearLayout,RelativeLayout,TextView,Button,ImageView,RecyclerView,ListView等;这些都是Android系统本身就提供好的,我们只需要拿过来使用就可以了,有时候我们需要自定义一些布局,那我们就需要知道View如何绘制到屏幕上?,以便我们能更好完成开发工作;
Activity启动以后如何完成View的绘制工作呢?首页我们就需要了解Activity启动以后的绘制View执行流程;
1.View绘制流程源码路径
1.1Activity加载ViewRootImpl
ActivityThread.handleResumeActivity()
--> WindowManagerImpl.addView(decorView, layoutParams)
--> WindowManagerGlobal.addView()
WindowManagerGlobal.addView()负责创建ViewRootImpl,同时执行root.setView(view, wparams, panelParentView);
1.2ViewRootImpl启动View树的遍历
ViewRootImpl.setView(decorView, layoutParams, parentView)
-->ViewRootImpl.requestLayout()
-->scheduleTraversals()
-->TraversalRunnable.run()
-->doTraversal()
-->performTraversals()(performMeasure、performLayout、performDraw)
以上是Activity启动以后View绘制的关键流程,任何一个视图都不是凭空突然出现在屏幕上,它们都是要经过非常科学的绘制流程才能显示出来,通过以上关键流程发现真正绘制是视图主要包含三个阶段performMeasure(测量),performMeasure(布局),performDraw(绘制),下面我们逐个对这三个阶段展开讨论;
2.View绘制流程
2.1measure()测量
measure是测量的意思,那么onMeasure()方法顾名思义就是用于测量视图的大小的。View系统的绘制流程会从ViewRoot的performTraversals()方法中开始,在其内部调用View的measure()方法。measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的测量模式和大小。
首先要了解视图的宽度和高度的测量模式和大小是使用MeasureSpec表示;
(1).MeasureSpec怎么表示视图的测量模式和大小
重写过onMeasure()方法都知道,测量需要用到MeasureSpec类获取View的测量模式和大小,那么这个类是怎样存储这两个信息呢?
留心观察的话会发现,onMeasure方法的两个参数实际是32位int类型数据,即:
00 000000 00000000 00000000 00000000
而其结构为 mode + size ,前2位为mode,而后30位为size;
如何生成MeasureSpec数据?
makeMeasureSpec()方法(mode + size --> measureSpec)
MeasureSpec
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
这里解释一下,按位或左侧为size的高2位清零后的结果,右侧为mode的低30位清零后的结果,两者按位或运算的结果正好为高2位mode、低30位size,例:
01000000 00000