简单梳理一下View的绘制流程。View的绘制分为三个部分(mechanism),分别为:measure,layout,draw。
第一部分:Measure
这一过程负责确定各个View
的大小,得到的结果存放在每个View
的mMeasuredWidth
,mMeasuredHeight
中。
该部分主要有三个函数:
measure(int widthMeasureSpec, int heightMeasureSpec);
onMeasure(int widthMeasureSpec, int heightMeasureSpec);
setMeasuredDimension(int measuredWidth, int measuredHeight);
measure(int, int)
发起measure过程,需要用到两类信息:
MeasureSpecs
,父View
对子View
的要求,有三种模式:UNSPECIFIED
,EXACTLY
,AT_MOST
ViewGroup.LayoutParams
,子View
自身的布局参数,可以是具体值、MATCH_PARENT
或WRAP_CONTENT
。
该方法为final
方法,不可被Override
,该方法会调用onMeasure()
,一般情况下子类应该Override
onMeasure()
方法。
onMeasure(int, int)
进行具体的测量工作,该函数的参数是mMeasuredWidth
和mMeasuredHeight
,即父View
对我们的测量要求,要做的工作就是根据父View
的大小确定自己的大小。如果是ViewGroup
,还要调用子View
的measure(int, int)
才能确定自己的大小。同时这一步的最后需要调用setMeasuredDimension(int, int)
将测量结果存储起来,否则在使用过程中会抛出异常。
setMeasuredDimension(int, int)
将测量结果存储在mMeasuredWidth
,mMeasuredHeight
中,在之后的步骤中,可以通过getMeasuredWidth()
和getMeasuredHeight()
获取这两个参数。
第二部分:Layout
这一部分通过Measure中得到的各个View
的大小,确定各个View
的摆放位置。
该部分主要有两个函数:
layout(int l, int t, int r, int b);
onLayout(boolean changed, int l, int t, int r, int b);
相对于measure,这部分比较简单,最后会通过setFrame(int, int, int, int)确定各个View相对于父View的座标。
第三部分:Draw
该部分绘制View的内容。
主要有两个函数:
draw(Canvas canvas);
onDraw(Canva canvas);
draw(Canvas)
代码略长,但主要工作是调用onDraw()
绘制自己,然后调用dispatchDraw()
绘制子View
。
根据文档说明,该方法负责渲染将View以及所有子View渲染到Canvas,必须在layout完成后调用,一般不应该Override该方法,如果要继承,可以Override onDraw()方法。
也就是说自定义View的时候基本可以不考虑这个方法。
onDraw(Canvas)
View
的onDraw()
方法为空实现,subclass一般要Override
该方法,绘制自己。
比如,想绘制一个矩形:
// 画一个起点座标(0,0),终点座标(10,20)的矩形
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, 10, 20, paint);
dispatchDraw(Canvas)
调用子View
的draw()
方法,绘制子View
。