View视图绘制基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。那我们具体来看一下。
measure(计算视图大小)
视图大小,准确应该是指视图的布局大小。我们在调用的时候应该注意以下几点:
measure()该方法被修饰为final,不允许重载,View子类只能通过重载onMeasure()来完善自己的测量逻辑。
measure(int widthMeasureSpec, int heightMeasureSpec)
,MeasureSpec作为输入参数,该值为int型,其值由两部分组成是一个32位的int型的数值,高2位代表规格模式specMode,低30位代表具体尺寸specSize。其中specMode有三种模式:- MeasureSpec.EXACTLY:确定模式,即父视图希望子视图的大小是确定的,由specSize决定;如200dp,match_parent。
- MeasureSpec.AT_MOST:最多模式,即父视图希望子视图的大小最多是specSize指定的值;如wrap_content。
- MeasureSpec.UNSPECIFIED:未指定模式,此时父视图完全尊重子视图的设计;系统
最顶层视图DecorView测量时的MeasureSpec从何而来呢,是在ViewRootImpl中调用getRootMeasureSpec()获得,LayoutParam宽高参数均为MATCH_PARENT。
视图的大小由父视图和子视图共同决定,比如LinearLayout的layout_height设置为wrap_content,视图大小由子视图决定;设置为0dp,视图大小的高度就为0。
根据子视图自己的测量规格调用child.measure()进行最终的测量;
layout(计算视图位置)
计算视图位置也可以说成是布局,视图的布局过程是从ViewRoot对象调调用根视图的layout()方法开始,接着layout()方法调用根视图的onLayout()方法,onLayout()方法会对所包含的子视图逐一执行layout操作,如果子视图是ViewGroup子类对象,则继续调用ViewGroup子视图的layout(),重复这一过程。如果子视图是View子类对象,则在子视图重载的onLayout()内部将自己布局到视图中,不需要对子视图进行layout操作,这样一次layout过程结束。我们来看看layout的调用流程:
1.首先调用layout(),如果继承View,layout()方法可被重载,如果继承ViewGroup,layout()修饰为final,不可重载;ViewGroup.onLayout()修饰为abstract,子类必须重载。我们来看看onLayout(boolean changed, int l, int t, int r, int b)
l,t,r,b分别表示什么呢?让下面这张图来告诉我们。
l,t,r,b分别对应的蓝色的getLeft(),getTop(),getRight(),getBottom。
2.调用setFrame(l,t,r,b)会将位置信息保存起来,这些参数将会保存到View的内部变量 (mLeft、mTop、mRight、mBottom)中。在保存完变量前,会先对比这些参数是否和原来的相同,如果相同,则什么都不做,如果不同则进行重新赋值,并在赋值前给mPrivateFlags中添加DRAWN标识,同时调用invalidate()通知View系统原来占用的位置需要重绘。
3.调用onLayout(),View中定义的onLayout()方法默认什么都不做,View系统提供onLayout()方法的目的是为了使系统包含的子视图的父视图能够在onLayout()方法对子视图进行位置分配,正因为如此,如果是父视图,则必须重写onLayout(),也正因为如此ViewGroup类才会把onLayout重载改成了abstract类型。
4.清除mPrivateFlags中的LAYOUT_REQUIRED标识,因为layout操作已经完成。
draw(绘制视图)
绘制过程就是把View对象绘制到屏幕上,如果该View是一个容器ViewGroup,则需要递归绘制其所包含的所有子视图;绘制的流程如下:
View背景Backgroud:每个视图都可以有一个背景,背景可以是一个颜色值,也可以是一副图片,甚至可以是任何Drawable对象;
视图自身的内容:一般由视图设计者在视图的onDraw方法中完成具体的内容绘制,比如TextView的内容就是具体的文字,若是视图容器ViewGroup,则需要递归完成子视图的具体内容绘制;
渐变边框FadingEdge:其作用是为了让视图的边框看起来更有层次感,其本质就是一个Shader对象,当然可以通过配置项关闭该效果;
滚动条ScrollBar:用来显示当前滚动的位置和状态;