Android中View的绘制流程

Android中View的绘制流程

View的绘制流程是从ViewRootImpl的performTraversals方法开始,它经过measure、layout和draw三个过程才能最终将一个View绘制出来。

1、measure,即测量的意思,measure方法是在View中,它是final类型,意味着android系统不允许我们重写该方法,即ViewGroup中也找不到该方法。View在执行完measure测量方法后,我们就可以使用getMeasuredWidth()和getMeasuredHeight()获得View的测量宽高,在之前都是0,但并不一定是View最终显示出来的具体大小。

2、layout,最终确定View的位置,跟measure不同的是,View中的layout不是final类型,允许子类重写,但ViewGroup中layout是final类型,不允许子类重写。layout执行完成后,我们就可以使用getWidth和getHeight获得View最后显示在屏幕上的宽高了。(关于getMeasureHeight和getWidth的区别,读者请自行查资料,这里就不阐述了)

3、draw,最后一个步骤,把View绘制到屏幕上,draw其实可以拆分成很多步骤,首先drawBackground,绘制背景,如果有需要的话;其次绘制自己onDraw;然后调用dispatchDraw绘制子View;最后执行onDrawForeground绘制前景或着scrollbar等。

接下来我们要理解一个概念:MeasureSpec,它包含了两个信息,测量模式(SpecMode)和测量规格(SpecSize),SpecMode有三种模式:

(1)、MeasureSpec.EXACTLY:父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式。

(2)、MeasureSpec.AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content。

(3)、MeasureSpec.UNSPECIFIED:父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量状态,比较少用。

一般我们使用getMeasuredWidth()和getMeasuredHeight()获得View的测量宽高的值就包含测试模式和测量规格两个值,系统也提供了方法用于分解:
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
反之,也有组合的方法:
public static int makeSafeMeasureSpec(int size, int mode) {
if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
return 0;
}
return makeMeasureSpec(size, mode);
}
这三个方法都是属于MeasureSpec中的静态方法,为什么要了解这个概念呢,因为我们在自定义View的时候,往往会重写onMeasure方法,这时候你就需要对此有所理解。

View的绘制过程是先调用measure方法,对自身进行测量,该方法内部会调用onMeasure方法,onMeasure方法是在View中定义的,代码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
方法中就实现了设置测量后View的高宽操作,View中的measure、onMeasure方法并没有对子View进行测量,而ViewGroup也没有重写onMeasure方法,该方法一般在具体的ViewGroup实现类被重写,如LinearLayout、RelativeLayout等就重写了onMeasure方法,一般情况下,对子View的测量是在onMeasure方法中实现的,这里就有很多人会问:为什么ViewGroup不重写该方法对子View进行测量呢,那是因为不同ViewGroup对子View的测量方式不相同,所以并不能在ViewGroup写死。如果我们自定义View继承了ViewGroup并且有子View,我们必须在重写的onMeasure的方法中对子View进行measure。

接下来会调用layout方法,layout用以确定自身的位置,方法内部会调用onLayout方法,onLayout在ViewGroup是一个抽象方法,所有继承ViewGroup都要实现该方法,并且对子View进行布局,如果有你有子View的话。LinearLayout、RelativeLayout等就实现了onLayout方法,如同ViewGroup没有实现onMeasure一样,因为它们对子View的布局约束不一样,需交给具体ViewGroup去实现。

经过measure、layout后,view的最后一步就是绘制了,ViewGroup已经重写View的dispatchDraw对子View进行分发draw,对于我们自定义View来讲,一般情况是重写onDraw来改变本身的绘制。

经过上面三个流程,View的绘制就算完成了,值得注意的是,由于布局的复杂性,View的测量可能会调用多次。

最后,View中有一个特殊的方法setWillNotDraw,这个方法的意义是:如果一个View不需要绘制任何内容,设置这个标记位为true后,系统会进行相应的优化。默认情况下,View没有启用这个标记位,但是ViewGroup默认启用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值