1. 简介
View
的绘制过程分为三部分:measure
、layout
、draw
。
measure
用来测量View的宽和高。
layout
用来计算View的位置。
draw
用来绘制View。
-
经过
measure
之后就进入了layout
过程,measure
过程可以查看这篇文章:自定义View原理篇(1)-measure过程。 -
本章主要对
layout
过程进行详细的分析。 -
本文源码基于android 27。
2. layout的始点
跟measure
一样,layout
也是始于ViewRootImpl
的performTraversals()
:
2.1 ViewRootImpl的performTraversals
private void performTraversals() {
//...
//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行测量
//...
performLayout(lp, mWidth, mHeight);//执行布局
//...
performDraw();//执行绘制
//...
}
再来看看performLayout()
:
2.2 ViewRootImpl的performLayout
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
//...
//调用layout
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//...
}
这里的host
就是DecorView
,如果不知道DecorView
,可以看看这篇文章:从setContentView揭开DecorView。
layout()
方法传入的0,0,host.getMeasuredWidth,host.getMeasuredHeight
就是一个View
的上下左右四个位置,可以看到,DecorView
都是从左上角位置(0,0)开始进行布局的,其宽高则为测量宽高。
下面重点来分析Layout
过程
3.layout过程分析
layout
用来计算View
的位置,即确定View
的Left
、Top
、Right
和 Bottom
这四个顶点的位置。如下图所示:
[外链图片转存失败(img-nd7ZrEmp-1568000248117)(http://upload-images.jianshu.io/upload_images/6163786-452eb2cc673d8ab0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
同样,layout
过程根据View
的类型也可以分为两种情况:
- 计算单一
View
位置时,只需计算其自身即可;- 计算
ViewGroup
位置时,需要计算ViewGroup
自身的位置以及其包含的子View
在ViewGroup
中的位置。
我们对这两种情况分别进行分析。
3.1 单一View的layout过程
单一View
的layout
过程是从View
的layout()
方法开始:
3.1.1 View的layout
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT)