今天我们来复习下View的绘制流程。View是Android中所有控件的基类,是控件的一种抽象,代表了一个控件。
一、View树的绘制流程
measure –> layout –> draw
测量 –> 布局 –>绘制
measure :测量出View的宽/高;
layout:确定View最终宽/高四个顶点的位置;
draw:将View绘制在屏幕上;
二、measure
1、ViewGroup.LayoutParams:
指定视图宽度、高度的参数有 match_parent、wrap_content、固定值(100dp)三种。
2、MeasureSpec:
测量规则,代表一个32位的int值,将SpecMode、SpecSize包装成一个int值来避免过多的对象内存分配;高2位代表SpecMode(测量模式), 低30位代表SpecSize(在某种测量模式下测量出的具体值)。
SpecMode 测量模式:
- UNSPECIFIED:
父容器不对View有任何限制,要多大给多大,一般用于系统内部,表示一种测量状态。 - EXACTLY:
父容器检测出View所需要的精确大小,这时候View的值就是SpecSize;它对应于LayoutParams中的 match_parent 和 具体的数值两种模式。 - AT_MOST:
父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值;它对应于LayoutParams中的 wrap_content 模式。
结论:
子View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定的。
3、measure的重要方法
- measure(int widthMeasureSpec, int heightMeasureSpec)
是一个final 方法子类无法重写,会调用onMeasure方法,我们可以在onMeasure方法中处理测量的逻辑。 - onMeasure(int widthMeasureSpec, int heightMeasureSpec)
在onMeasure方法中处理测量的逻辑。 - setMeasuredDimension(int measuredWidth, int measuredHeight)
测量的终极方法,测量完成后进行调用,传入测量出来的的宽、高值。
三、layout
- public void layout(int l, int t, int r, int b){}
确定View本身的位置。 - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }
确定所有子元素的位置;
父容器的位置确定后,它在onLayout中会遍历所有的子元素并调用其layout方法,在layout方法中会调用onLayout方法。
四、draw
1、将View绘制在屏幕上,遵循如下几步:
1、绘制背景:drawBackground(canvas);
2、绘制自己 : onDraw(canvas);
3、绘制childen :dispatchDraw(canvas);
4、绘制装饰: onDrawForeground(canvas);
2、invalidate()、requestLayout()
invalidate()和requestLayout(),常用于View重绘和更新,其主要区别如下:
- invalidate方法只会执行onDraw方法;
- requestLayout方法只会执行onMeasure方法和onLayout方法,并不会执行onDraw方法;
所以当我们进行View更新时,若仅View的显示内容发生改变且新显示内容不影响View的大小、位置,则只需调用invalidate方法;若View宽高、位置发生改变且显示内容不变,只需调用requestLayout方法;若两者均发生改变,则需调用两者,按照View的绘制流程,推荐先调用requestLayout方法再调用invalidate方法。
3、invalidate() 和 postInvalidate()
invalidate方法用于UI线程中重新绘制视图
postInvalidate方法用于非UI线程中重新绘制视图,省去使用handler