measure基本流程
- View的绘制流程是从
ViewRoot
的performTraversals
方法开始的。它经过measure、layout和draw三个过程才能最终将一个View绘制出来。
- measure:测量View的宽和高
- layout:确定View在父容器的位置
- draw:将View绘制在屏幕上。
performTraversals
的工作流程:
- 父容器在
onMeasure
中,会遍历调用子View的measure
方法。 - 所有子View的
measure
完成后,才会进入layout过程,其调用过程与measure
类似。 layout
过程完成后,最后进入draw
过程,其过程与measure
类似,不过performDraw
的传递是在draw
方法中通过dispatchDraw
方法实现的。
- 父容器在
只有在Measure完成后,才能通过
getMeasureWidth
和getMeasureHeight
获取View测量后的宽高,它们在几乎所有的情况下等同于View的最终宽高,但特殊情况除外。- 同理,Layout完成后,才能拿到View的四个顶点的位置,并通过
getWidth
和getHeight
获取View的最终宽和高。
- 同理,Layout完成后,才能拿到View的四个顶点的位置,并通过
DecorView是顶级View。
- 一般情况下,它内部会包含一个竖直方向的线性布局,上面是标题栏,下面是内容区。
- 在Activity中
setContentView
所设置的布局文件,实际上是被加到内容区。 - 内容区的id是可以获得的,其ID是
android.R.id.content
,对第一个子View就是我们所设置的View。 - DecorView是一个
FramLayout
,View层的所有事件都先经过DecorView
,然后才逐级分发。
MeasureSpec
大致看看源码:
public static class MeasureSpec { /** @hide */ @IntDef({UNSPECIFIED, EXACTLY, AT_MOST}) @Retention(RetentionPolicy.SOURCE) public @interface MeasureSpecMode {} private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT; public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << View.MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } } @MeasureSpecMode public static int getMode(int measureSpec) { //noinspection ResourceType return (measureSpec & MODE_MASK); } public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); }
Android源码中大量使用了位运算,比如Intent的各种FLAG。它们拥有更高的效率和更低的内存开销。
SpecMode有三类:
EXACTLY
:layout_width
/layout_height
指定为具体数值或match_parent
,父容器就会指定一个精确的SpecSize。AT_MOST
:layout_width
/layout_height
指定为wrap_content
,父容器将指定一个可用大小的SpecSize,View的大小不能大于这个值。具体的值要看不同View的具体实现。UNSPECIFIED
:父容器不对View有任何限制。开发人员在绘制自定义View的时候会用到。
MeasureSpec
和LayoutParams
针对DecorView,其
MeasureSpec
由窗口的尺寸和其自身LayoutParams
共同决定。LayoutParams.MATCH_PARENT
: Window can’t resize. Froce root view to be windowSize. EXACTLY模式,(强制)大小就是窗口的大小。LayoutParams.WRAP_CONTENT
: Window can resize. Set max size for root view. AT_MOST模式,将为root view设置最大尺寸,但不能超过窗口大小。- 固定大小(比如具体dp值): Window wants to be an exact size.Force root view to be that size. EXACTLY模式,(强制)root view尺寸就是指定的大小。
而普通的View,则是由父容器的
MeasureSpec
和自身LayoutParams
共同决定,此外还与父容器的pading
和自身的margin
有关。
普通View的
MeasureSpec
的创建规则, 查看getChildMeasureSpec
源码即可理解:父Spec View尺寸 View的SpecSize View的SpecMode EXACTLY
固定值>=0 固定值 EXACTLY
EXACTLY
MATCH_PARENT
父SpecSize-padding EXACTLY
EXACTLY
WRAP_CONTENT
父SpecSize-padding AT_MOST
AT_MOST
固定值>=0 固定值 EXACTLY
AT_MOST
MATCH_PARENT
父SpecSize-padding AT_MOST
AT_MOST
WRAP_CONTENT
父SpecSize-padding AT_MOST
UNSPECIFIED
固定值>=0 固定值 EXACTLY
UNSPECIFIED
MATCH_PARENT
0 UNSPECIFIED
UNSPECIFIED
WRAP_CONTENT
0 UNSPECIFIED