ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

View的绘制流程

2020-02-25 09:42:59  阅读:272  来源: 互联网

标签:size MeasureSpec int 流程 measure View 绘制 view


View的绘制流程主要包括measure,layout,draw三大流程,measure用来确定view的测量宽/高,layout用来确定view的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上


Measure

  1. 如果只是一个原始的View,那么通过meaure方法就完成了其测量过程,如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子View的measure方法,各个子元素再去递归调用这个流程

  2. view的measure过程由measure方法来完成,measure方法是一个final方法,这意味着子类不能重写此方法,在View的measure方法中回去调用view的onMeasure方法

  3. 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_MOSTEXACTLY这两种情况,返回的大小就是MeasureSpec中的specSize,也就是View测量后的大小,直接继承view的自定义控件需要重写onMeasure方法并设置WRAP_CONTENT时自身的大小,否则在布局中使用WRAP_CONTENT就相当于MATCH_PARENT

  4. ViewGoup的measure过程

    对于ViewGroup来说,除了完成自己的measure过程外,还会遍历去调用所有子元素的measure方法,各个子元素再去递归执行这个过程,ViewGroup是一个抽象类,没有重写onMeasure方法,但是提供了measureChildrenmeasureChildmeasureChildWithMargins方法

    /**
     * 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

parade岁月 发布了72 篇原创文章 · 获赞 50 · 访问量 7万+ 私信 关注

标签:size,MeasureSpec,int,流程,measure,View,绘制,view
来源: https://blog.csdn.net/parade0393/article/details/104483460

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有