View的绘制流程
作者:互联网
View的绘制流程主要包括measure,layout,draw三大流程,measure用来确定view的测量宽/高,layout用来确定view的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上
Measure
-
如果只是一个原始的View,那么通过meaure方法就完成了其测量过程,如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子View的measure方法,各个子元素再去递归调用这个流程
-
view的measure过程由measure方法来完成,measure方法是一个final方法,这意味着子类不能重写此方法,在View的measure方法中回去调用view的onMeasure方法
-
onMeasure方法如下(直接继承原始View)
/** * <p> * 测量视图及其内容以确定测量的宽度和测量的高度,这个方法由measure方法调用,并且应该被子类重写,以 * 准确和有效的测量其内容 * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overridden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * * <p> * 当子类重写了此方法,则必须调用setMeasuredDimension方法来存储view的测量宽/高,如果不这样做将会触发由 *measure方法抛出的异常throw new IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); * <strong>CONTRACT:</strong> When overriding this method, you * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the * measured width and height of this view. Failure to do so will trigger an * <code>IllegalStateException</code>, thrown by * {@link #measure(int, int)}. Calling the superclass' * {@link #onMeasure(int, int)} is a valid use. * </p> * * <p> * The base class implementation of measure defaults to the background size, * unless a larger size is allowed by the MeasureSpec. Subclasses should * override {@link #onMeasure(int, int)} to provide better measurements of * their content. * </p> * * <p> * If this method is overridden, it is the subclass's responsibility to make * sure the measured height and width are at least the view's minimum height * and width ({@link #getSuggestedMinimumHeight()} and * {@link #getSuggestedMinimumWidth()}). * </p> * * @param widthMeasureSpec horizontal space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * @param heightMeasureSpec vertical space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * * @see #getMeasuredWidth() * @see #getMeasuredHeight() * @see #setMeasuredDimension(int, int) * @see #getSuggestedMinimumHeight() * @see #getSuggestedMinimumWidth() 取决于背景尺寸和minWidth的二者中的较大值minWidth默认为0 * @see android.view.View.MeasureSpec#getMode(int) * @see android.view.View.MeasureSpec#getSize(int) */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
onMeasure方法中必须调用
setMeasuredDimension
方法,否则将抛出异常,在这里我们只需关注getDefaultSize
方法/** * 一个用来返回默认大小的实用方法,如果MeasureSpec没有提供任何限制则用系统提供的尺寸(背景尺寸或者minWidth * 的尺寸取二者最大);如果MeasureSpec允许,尺寸将会变大 * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no constraints. Will get larger if allowed * by the MeasureSpec. * * @param size Default size for this view * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result;
可以看出getDefaultSize的逻辑很简单,我们只需要看
AT_MOST
和EXACTLY
这两种情况,返回的大小就是MeasureSpec中的specSize,也就是View测量后的大小,直接继承view的自定义控件需要重写onMeasure方法并设置WRAP_CONTENT时自身的大小,否则在布局中使用WRAP_CONTENT就相当于MATCH_PARENT -
ViewGoup的measure过程
对于ViewGroup来说,除了完成自己的measure过程外,还会遍历去调用所有子元素的measure方法,各个子元素再去递归执行这个过程,ViewGroup是一个抽象类,没有重写onMeasure方法,但是提供了
measureChildren
,measureChild
,measureChildWithMargins
方法/** * Ask all of the children of this view to measure themselves, taking into * account both the MeasureSpec requirements for this view and its padding. * We skip children that are in the GONE state The heavy lifting is done in * getChildMeasureSpec. * * @param widthMeasureSpec The width requirements for this view * @param heightMeasureSpec The height requirements for this view */ protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } } /** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding. * The heavy lifting is done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param parentHeightMeasureSpec The height requirements for this view */ protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
measureChild的思想就是就是取出子元素的LayoutParams,然后再通过
getChildMeasureSpec
来创建子元素的MeasureSpec从而传递给子元素的measure方法。ViewGroup并没有onMeasure方法,这是因为ViewGroup是一个抽象类,其测量过程需要各个子类去具体实现,比如LinearLayout、RelativeLayout都有不同的布局特征,它们在onMeasure中都会调用measureChildWithMargins
或者measureChild
去传递测量过程,最终传递到单一View的onMeasure
标签:size,MeasureSpec,int,流程,measure,View,绘制,view 来源: https://blog.csdn.net/parade0393/article/details/104483460