Android视图绘制流程完全解析,带你一步步深入了解View(二) —站在巨人的肩膀上学习总结
详情请看,郭林大神博客 http://blog.csdn.net/guolin_blog/article/details/16330267
总结:
1,measure方法中onMeasure()
(这里才是测量并设置View大小的地方,默认会调用getDefaultSize()方法来获取视图的大小)
2,layout方法中onLayout()
(onLayout是一个空实现,因为onLayout()过程是为了确定视图在布局中所在的位置,
而这个操作应该是由布局来完成的,即父视图决定子视图的显示位置)
3,draw()方法中onDraw()
onDraw()是一个空方法因为每个视图的内容部分肯定都是各不相同的,
这部分的功能交给子类来去实现也是理所当然的。
一:onMeasure()
1,View系统的绘制是从Viewroot的preformTraversals()方法开始执行的
其实它的内部也是调用了View的measure()方法,该方法的两个参数widthMeasureSpec和heightMeasureSpec这两个值分别确定试图的宽高的规格和大小
MeasureSpec 的值由specSize(大小)和specMode(规格)共同组成
规格有三种:
1. EXACTLY === MATCH_PARENT 填充
表示父视图希望子视图的大小应该是由specSize的值来决定的,
系统默认会按照这个规则来设置子视图的大小,
开发人员当然也可以按照自己的意愿设置成任意的大小。
2. AT_MOST === WRAP_CONTENT 包裹
表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,
并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,
开发人员当然也可以按照自己的意愿设置成任意的大小。
3. UNSPECIFIED
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。
这种情况比较少见,不太会用到。
####2,onMeasure()方法这里才是真正去测量并设置View大小的地方,
默认会调用getDefaultSize()方法来获取视图的大小
总结一:
1,子试图绘制规矩
子试图的绘制由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,
而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。
在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()
来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0
2,onMeasure(widthMeasureSpec,heightMeasureSpec)的两个参数是父布局
经过计算后传递给子试图的.
3,但是最外层的根视图,它的widthMeasureSpec和heightMeasureSpec又是从哪里得到的呢?
getRootMeasureSpec()获取得到widthMeasureSpec和heightMeasureSpec(源码中就给赋值了)
4,如何自定义一个MeasureSpec()?实现对子试图的从新测量?
//这样就重新定义了规则
MeasureSpec.makeMeasureSpec()方法重新组装一个MeasureSpec(specSize,specMode)(重要)
//请求重新绘制
requestLayout()
5,一个布局的绘制可能有多次的measure,因为子View可能很多
ViewGroup中定义了一个measureChildren()方法来去测量子视图的大小
这里首先会去遍历当前布局下的所有子视图,然后逐个调用measureChild()方法来测量相应子视图的大小
二. onLayout()
1,measure之后,试图大小就已经测量出来了,接下来就是确定试图位置了ViewRoot的performTraversals方法
会在measure之后用layout方法
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
layout()方法接收四个参数,分别代表着左、上、右、下的坐标,
当然这个坐标是相对于当前视图的父视图而言的
2,在layout()方法中调用setFrame方法判断试图大小是否改变,是否需要从新measure
####3,onLayout()方法就是一个空方法因为onLayout()过程是为了确定视图在布局中所在的位置,
而这个操作应该是由布局来完成的,即父视图决定子视图的显示位置
总结二:
1,在onLayout()过程结束后,我们就可以调用getWidth()方法和getHeight()方法来获取视图的宽高了
重要2,getMeasureWidth()方法在measure()过程结束后就可以获取到了
而getWidth()方法要在layout()过程结束后才能获取到
getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,
而getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。
三. onDraw()
measure和layout的过程都结束后,接下来就进入到draw的过程了,在这里才真正地开始对视图进行绘制
ViewRoot中的代码会继续执行并创建出一个Canvas对象,然后调用View的draw()方法来执行具体的绘制工作
draw()方法内部的绘制过程总共可以分为六步
1,对视图背景的绘制
代码设置,setBackgroundColor()、setBackgroundResource()等方法进行赋值。
2,不重要省略
3,对视图的内容进行绘制,这里是调用onDraw()方法,这个方法是空的,每个试图内容不同,所以该功能
交给子类实现
4,这一步的作用是对当前视图的所有子视图进行绘制。但如果当前的视图没有子视图,那么也就不需要进行绘制了
5,不重要省略
6这一步的作用是对视图的滚动条进行绘制(view都有的,只是隐藏了)