上篇中我们分析到view视图的渲染主要是由ViewRootImpl负责,系统调用了performTraveals开始了view的测量、布局和绘制三大流程,后来又接着分析了其中最关键的测量流程,也就是方法performMeasure的执行过程,其中最主要的讲了一个View的内部类MeasureSpace,负责View测量的规则,最后详细分析了DecorView的测量过程;这篇我们接着分析普通view的测量过程。
1、ViewGroup的测量过程:
由于DecorView继承自FrameLayout,是PhoneWindow的一个内部类,而FrameLayout没有measure方法,因此调用的是父类View的measure方法,我们直接看它的源码,View#measure
它在内部调用了onMeasure方法,由于DecorView是FrameLayout子类,因此它实际上调用的是DecorView#onMeasure方法。在该方法内部,主要是进行了一些判断,这里不展开来看了,到最后会调用到super.onMeasure方法,即FrameLayout#onMeasure方法。
/frameworks/base/core/java/android/widget/FrameLayout.java
在保存测量结果时调用了setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
内部调用了resolveSizeAndState方法,这是父类view的方法
/frameworks/base/core/java/android/view/View.java
当specMode是EXACTLY时,那么直接返回MeasureSpec里面的宽高规格,作为最终的测量宽高;当specMode时AT_MOST时,那么取MeasureSpec的宽高规格和size的最小值。(注:这里的size,对于FrameLayout来说,是其最大子View的测量宽高)。
以上我们以DecorView为切入点,把ViewGroup的测量流程详细地分析了一遍,在ViewRootImpl.performTraversals()中获得DecorView的尺寸,然后在performMeasure()方法中开始测量流程,对于不同的layout布局有着不同的实现方式,但大体上是在onMeasure()方法中,对每一个子View进行遍历,根据ViewGroup的MeasureSpec及子View的layoutParams来确定自身的测量宽高,然后最后根据所有子View的测量宽高信息再确定父容器的测量宽高。
2、view的测量过程:
在上面分析时,我们知道FrameLayout在测量子view时,调用了measureChildWithMargins方法,主要参数是子View以及父容器的MeasureSpec,所以它的作用就是对子View进行测量,查看源码:
这里又接着调用了getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight+ lp.leftMargin + lp.rightMargin)来获取子view的宽高,接着看View.getChildMeasureSpec方法,
此方法根据父容器的MeasureSpec和子View的LayoutParams来决定子view的规格尺寸模式等。按不同的测量模式有以下几种组合:
获取到子view的宽高MeasureSpec后调用了方法child,measure(childWidthMeasureSpec,childHeightMeasureSpec),此时应该执行的是View的measure方法,将测量流程传递至View父类中,
此方法中进行一系列判断后调用 onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension方法是用来保存测量结果的,实际测量方法是getDefaultSize
此方法根据不同的测量模式返回不同的测量结果
当specMode 为MeasureSpec.AT_MOST、MeasureSpec.EXACTLY时直接返回specSize,也就是说当一个继承自View的自定义View,不管它设置wrap_content还是match_parent结果都是一样的,UNSPECIFIED模式,这个模式可能比较少见,一般用于系统内部测量,它直接返回的是size,而不是specSize,那么size从哪里来的呢?再往上看一层,它来自于getSuggestedMinimumWidth()或getSuggestedMinimumHeight(),我们选取其中一个方法,看看源码,View.getSuggestedMinimumWidth:
当View没有设置背景的时候,返回mMinWidth,该值对应于android:minWidth属性;如果设置了背景,那么返回mMinWidth和mBackground.getMinimumWidth中的最大值。那么mBackground.getMinimumWidth又是什么呢?其实它代表了背景的原始宽度,比如对于一个Bitmap来说,它的原始宽度就是图片的尺寸。
网友评论