概述
当启动一个应用时,会启动一个主Activity,Android系统会根据Activity的布局对它进行绘制。绘制会从根视图ViewRoot开始,从上到下遍历整个视图树,每个View控制负责绘制自己,而ViewGroup还需负责通知自己的子View进行绘制操作。
视图View的绘制操作过程整体上可以分为三个操作部分:测量(Measure),布局(Layout),绘制(Draw)。
MeasureSpec
MeasureSpec是View类中的一个静态内部类,用来说明应该怎么测量这个View,包括有测量模式和测量模式下的尺寸大小SpecSize。
MeasureSpec表示的是一个32位的整数值,他的高两位表示测量模式,低位表示测量的规格大小。
测量模式包括三种:
- UpSecified(不指定测量模式):父视图没有限制子视图大小。
- Exactly(精准测量模式):当该视图的layout_width或者layout_height指定为具体数值或者match_parent时或具体值时生效,表示父视图已经决定了子视图的精确大小。
- At_Most(最大值模式):当前视图的宽高制定为wrap_content时生效,此时子视图的尺寸不可超过俯视图的最大尺寸。
Measure
Measure用来计算View的实际大小,页面的测量从preformMeasure方法开始。
具体操作是分发给ViewGroup的,由ViewGroup在它的measureChild方法中传递给自View,ViewGroup通过便利自己所有子View,并逐个调用子View的measure方法实现测量操作。
View的measure方法,最终的测量是通过回调onMeasure方法实现的,逐个由各个View的子类进行实现,可以通过重写这个方法实现自定义View。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
....
}
// 如果需要自定义测量,子类需重写这个方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
// 如果 View 没有重写onMeasure 方法,默认会直接调用 getDefaultSize
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Layout
Layout过程用来确定View在父容器的布局位置,他是父容器获取子View的位置参数后,调用子View的layout方法并将位置参数传入实现的。
View的layout方法是调用了onLayout方法,onLayout方法中是空方法,由View中的ViewGroup子类进行实现。
自定义ViewGroup方法需要重写onLayout方法。
public void layout(int l, int t, int r, int b) {
onLayout(changed, l, t, r, b);
}
// 空方法,子类如果是 ViewGroup 类型,则重写这个方法,实现 ViewGroup 中所有 View 控件布局
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
Draw
Draw操作是将View控件进行绘制出来,绘制的流程从performDraw方法开始,最终调用到了每个View的draw方法进行绘制每个具体的View。
绘制基本上可以分为六个步骤:
- 对View的背景进行绘制(drawBackground方法)
- 保存当前的图层信息(可跳过)
- 绘制View的内容(调用onDraw方法,View类是空实现,不同View中有不同内容)
- 对View的子View进行绘制(如果有子View)(调用dispatchDraw方法,View勒种为空,ViewGroup中重写)
- 绘制VIew的褪色的边缘,类似于阴影效果(可跳过)
- 绘制View的装饰(如滚动条等)
网友评论