Android View的绘制流程,androidview绘制
转自:Android View的绘制流程
写得太好了,本来还想自己写的,奈何肚里墨水有限,直接转吧。正所谓前人种树,后人乘凉。。
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
【转载请明显注明出处,尊重劳动成果】
Measure过程
对于测量我们来说几个知识点,了解这几个知识点,之后的实例分析你才看得懂。
1、MeasureSpec 的理解
对于View的测量,肯定会和MeasureSpec接触,MeasureSpec是两个单词组成,翻译过来“测量规格”或者“测量参数”,很多博客包括官方文档对他的说明基本都是“一个MeasureSpec封装了从父容器传递给子容器的布局要求”,这个MeasureSpec 封装的是父容器传递给子容器的布局要求,而不是父容器对子容器的布局要求,“传递” 两个字很重要,更精确的说法应该这个MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算得出一个针对子View的测量要求,这个测量要求就是MeasureSpec。
- 大家都知道一个MeasureSpec是一个大小跟模式的组合值,MeasureSpec中的值是一个整型(32位)将size和mode打包成一个Int型,其中高两位是mode,后面30位存的是size,是为了减少对象的分配开支。MeasureSpec 类似于下图,只不过这边用的是十进制的数,而MeasureSpec 是二进制存储的。
注:-1 代表的是EXACTLY,-2 是AT_MOST - MeasureSpec一共有三种模式
UPSPECIFIED : 父容器对于子容器没有任何限制,子容器想要多大就多大
EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。
AT_MOST:子容器可以是声明大小内的任意大小
很多文章都会把这三个模式说成这样,当然其实包括官方文档也是这样表达的,但是这样并不好理解。特别是如果把这三种模式又和MATCH_PARENT和WRAP_CONTENT 联系到一起,很多人就懵逼了。如果从代码上来看view.measure(int widthMeasureSpec, int heightMeasureSpec) 的两个MeasureSpec是父类传递过来的,但并不是完全是父View的要求,而是父View的MeasureSpec和子View自己的LayoutParams共同决定的,而子View的LayoutParams其实就是我们在xml写的时候设置的layout_width和layout_height 转化而来的。我们先来看代码会清晰一些:
父View的measure的过程会先测量子View,等子View测量结果出来后,再来测量自己,上面的measureChildWithMargins就是用来测量某个子View的,我们来分析是怎样测量的,具体看注释:
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { // 子View的LayoutParams,你在xml的layout_width和layout_height, // layout_xxx的值最后都会封装到这个个LayoutParams。 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //根据父View的测量规格和父View自己的Padding, //还有子View的Margin和已经用掉的空间大小(widthUsed),就能算出子View的MeasureSpec,具体计算过程看getChildMeasureSpec方法。 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); //通过父View的MeasureSpec和子View的自己LayoutParams的计算,算出子View的MeasureSpec,然后父容器传递给子容器的 // 然后让子View用这个MeasureSpec(一个测量要求,比如不能超过多大)去测量自己,如果子View是ViewGroup 那还会递归往下测量。 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } // spec参数 表示父View的MeasureSpec // padding参数 父View的Padding+子View的Margin,父View的大小减去这些边距,才能精确算出 // 子View的MeasureSpec的size // childDimension参数 表示该子View内部LayoutParams属性的值(lp.width或者lp.height) // 可以是wrap_content、match_parent、一个精确指(an exactly size), public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); //获得父View的mode int specSize = MeasureSpec.getSize(spec); //获得父View的大小 //父View的大小-自己的Padding+子View的Margin,得到值才是子View的大小。 int size = Math.max(0, specSize - padding); int resultSize = 0; //初始化值,最后通过这个两个值生成子View的MeasureSpec int resultMode = 0; //初始化值,最后通过这个两个值生成子View的MeasureSpec switch (specMode) { // Parent has imposed an exact size on us //1、父View是EXACTLY的 ! case MeasureSpec.EXACTLY: //1.1、子View的width或height是个精确值 (an exactly size) if (childDimension >= 0) { resultSize = childDimension; //size为精确值 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY 。 } //1.2、子View的width或height为 MATCH_PARENT/FILL_PARENT else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY 。 } //1.3、子View的width或height为 WRAP_CONTENT else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.AT_MOST; //mode为AT_MOST 。 } break; // Parent has imposed a maximum size on us //2、父View是AT_MOST的 ! case MeasureSpec.AT_MOST: //2.1、子View的width或height是个精确值 (an exactly size) if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; //size为精确值 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY 。 } //2.2、子View的width或height为 MATCH_PARENT/FILL_PARENT else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.AT_MOST; //mode为AT_MOST } //2.3、子View的width或height为 WRAP_CONTENT else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.AT_MOST; //mode为AT_MOST } break; // Parent asked to see how big we want to be //3、父View是UNSPECIFIED的 ! case MeasureSpec.UNSPECIFIED: //3.1、子View的width或height是个精确值 (an exactly size) if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; //size为精确值 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY