安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)

目录

一.MeasureSpec

  1. MeasureSpec基础
  2. MeasureSpec和LayoutParams关系

二.View的绘制流程

  1. onMeasure()
  2. onLayout()
  3. onDraw()

三.自定义View

  1. 自定义view分类
  2. 自定义view步骤
  3. 自定义view的注意事项

一.MeasureSpec

1.MeasureSpec基础

(1)组成:MeasureSpec是一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize( 某种测量模式下的规格大小)。

 

(2)三种模式:

  • UNSPECIFIED:父容器不对View有任何限制,要多大有多大。常用于系统内部。
  • EXACTLY(精确模式):父视图为子视图指定一个确切的尺寸SpecSize。对应LyaoutParams中的match_parent或具体数值。
  • AT_MOST(最大模式):父容器为子视图指定一个最大尺寸SpecSize,View的大小不能大于这个值。对应LayoutParams中的wrap_content。

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第1张图片

(图片来自厘米姑娘)

 

2.MeasureSpec和LayoutParams关系

顶级View(即DecorView):其MeasureSpec由窗口的尺寸和自身的LayoutParams决定

子View:MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。

 

二.View的绘制流程

1.总的流程:measure->layout->draw

  • measure确定View的测量宽高
  • layout确定View的最终宽高和四个顶点的位置
  • draw将View 绘制到屏幕
  • 对应onMeasure()、onLayout()、onDraw()三个方法。

具体过程:

  • ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带。
  • View的绘制流程是从ViewRoot和performTraversals开始。
  • performTraversals()依次调用performMeasure()、performLayout()和performDraw()三个方法,分别完成顶级 View的绘制。
  • 其中,performMeasure()会调用measure(),measure()中又调用onMeasure(),实现对其所有子元素的measure过程,这样就完成了一次measure过程;接着子元素会重复父容器的measure过程,如此反复至完成整个View树的遍历。layout和draw同理。过程图如下:

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第2张图片

 

2.onMeasure:确定测量宽高,但是不是最终的宽高(layout中可以设置宽高,但是一般onMeasure中的测量宽高就是最终宽高)

(1)子View:通过measure()即可完成测量

 

注意:

  • 继承自View的自定义控件设置为wrap_content时,其SpecMode=AT_MOST模式,根据上表可知其MeasureSpec=AT_MOST+parentSize。所以展示出来的效果为match_parent。
  • 继承自View的自定义控件需要重写onMeasure()设置wrap_content时的默认大小(防止展示效果为match_parent),重写过程如下
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        //分析模式,根据不同的模式来设置
        if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(mWidth,mHeight);
        }else if(widthSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(mWidth,heightSpecSize);
        }else if(heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSpecSize,mHeight);
        }
    }

 

(2)ViewGroup的measure:除了完成ViewGroup自身的测量外,还回去遍历调用子view的neasure方法

ViewGroup中没有重写onMeasure(),而是提供measureChildren()。

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第3张图片

 

3.layout过程:确定View的最终宽高和四个顶点的位置

大致流程:从顶级View开始依次调用layout(),其中子View的layout()会调用setFrame()来设定自己的四个顶点(mLeft、mRight、mTop、mBottom),接着调用onLayout()来确定其坐标,注意该方法是空方法,因为不同的ViewGroup对其子View的布局是不相同的。

最终宽高:一般情况下为上一步的测量宽高,但是用户可以重写onLayout中确定最终宽高。如下代码会使最终宽高比测量宽高大100px

public void layout(int l,int t,int r,int b){
    super.layout(l,t,r+100,b+100);
}

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第4张图片

 

4.draw过程:绘制到屏幕

绘制顺序:

  • 绘制背景:background.draw(canvas)
  • 绘制自己:onDraw(canvas)
  • 绘制children:dispatchDraw(canvas)
  • 绘制装饰:onDrawScrollBars(canvas)

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第5张图片

注意:

  • Vew有一个特殊的方法setWillNotDraw(),该方法用于设置 WILL_NOT_DRAW 标记位(其作用是当一个View不需要绘制内容时,系统可进行相应优化)。
  • 默认情况下View是没有这个优化标志的(设为true)。
  • 默认情况下ViewGroup有这个优化标志的(设为false)。所以当ViewGroup却需要绘制内容时,该标志位不起作用,需要显示的关闭WILL_NOT_DRAW (设为true)

 

三.自定义View

1.自定义View分类

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第6张图片

注:

继承View需要重写onMeasure,使得其支持wrap_content

重写onDraw,使得其支持padding

 

2.自定义View步骤

(1)自定义属性,xml文件中指定,View的构造函数中得到属性组,在得到具体的属性做操作。

(2)重写onMeasure和onDraw对wrap_content和padding做特殊处理

 

3.注意事项

安卓开发艺术笔记 | View的工作原理(绘制流程和自定义View)_第7张图片

 

 

你可能感兴趣的:(安卓开发)