View篇之Measure过程

作者: 雾里看花六月天 | 来源:发表于2018-04-07 16:28 被阅读28次
    1. 简介

    •View的绘制过程分为三部分:measure、layout、draw。

    measure用来测量View的宽和高。
    layout用来计算View的位置。
    draw用来绘制View。

    •本章主要对measure过程进行详细的分析。

    1. measure的始点

    measure是从ViewRootImpl的performTraversals()方法开始的:

    2.1 ViewRootImpl的performTraversals

    private void performTraversals() {
    
        //...
    
        //获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行测量
    
        //...
    }
    

    首先会获取view宽高的测量规格,测量规格在下一节会详细讲述,然后就是调用performMeasure()了:

    2.2 ViewRootImpl的performMeasure

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    
        //...
    
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    
        //...
    }
    

    performMeasure()中就是调用View的measure()方法开始进行测量。

    3.MeasureSpec

    了解measure的过程时,我们须先了解MeasureSpec。MeasureSpec,顾名思义,就是测量规格,其决定了一个View的宽和高。

    3.1 MeasureSpec组成

    MeasureSpec代表一个32位的int值,前2位代表SpecMode,后30位代表SpecSize。其中:SpecMode代表测量的模式,SpecSize值在某种测量模式下的规格大小。来张图解说明:


    测量说明.png

    3.2 SpecMode测量模式

    测量模式分为三种UNSPECIFIED、EXACTLY、AT_MOST,其具体说明如下表所示:

    测量模式 说明 应用场景
    UNSPECIFIED (未指定) 父容器没有对当前View有任何限制,当前View可以任意取尺寸 系统内部
    EXACTLY(精确) 父容器已经确定当前View的大小,无论View想要多大都会在这范围内 match_parent 和 体数值
    AT_MOST(最多) 当前View不能超过父容器规格大小,具体数值由view去决定 wrap-content

    3.3 MeasureSpec源码分析

    public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;//模式移位数
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;//模式掩码
    
        //UNSPECIFIED模式:父容器没有对当前View有任何限制,当前View可以任意取尺寸
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    
        //EXACTLY模式:父容器已经确定当前View的大小,无论View想要多大都会在这范围内
        public static final int EXACTLY     = 1 << MODE_SHIFT;
    
        //AT_MOST模式:当前View不能超过父容器规格大小,具体数值由view去决定
        public static final int AT_MOST     = 2 << MODE_SHIFT;
    
        //根据提供的size和mode得到一个测量规格
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            //sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
            //即targetSdkVersion<=17时,size与mode是直接相加的;>17则进行位运算
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);//位运算
            }
        }
    
        //根据提供的size和mode得到一个测量规格 
        public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }
    
        //获取测量模式
        @MeasureSpecMode
        public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }
    
        //获取测量大小
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
    
    }
    

    可以看到,MeasureSpec类还是挺简单的,MeasureSpec类通过将mode和size打包成一个32位int值来减少了对象内存分配,并提供了打包和解包的方法。

    3.4 确定MeasureSpec值

    在measure过程中,系统会将View的LayoutParams和父容器所施加的规则转换成对应的MeasureSpec,然后在onMeasure()方法中根据这个MeasureSpec来确定View的测量宽高。父容器所施加的规则对于DecorView与普通View是不同的,我们分开来看。

    3.4.1 确定DecorView的MeasureSpec值

    DecorView,作为顶级的View,我们平时setContentView()所设置的布局可能只是DecorView其中的一部分,如下图所示:


    DecorView、ContentView、标题栏图解

    3.4.1.1 ViewRootImpl的PerformTraveals

    在ViewRootImpl的PerformTraveals()方法中会获得DecorView的MeasureSpec值:

    private void performTraversals() {
    
        //...
    
        //获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行测量
    
        //...
    }
    

    3.4.1.2 ViewRootImpl的getRootMeasureSpec

    我们再来看看getRootMeasureSpec()方法:

    private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {
    
        case ViewGroup.LayoutParams.MATCH_PARENT:
            //如果View的布局参数为MATCH_PARENT,则其为精确模式,大小为窗口的大小
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            //如果View的布局参数为WRAP_CONTENT,则其为最多模式,大小不定,但不能超过窗口的大小
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            //如果View的布局参数为准确的数值,如100dp等,则其为精确模式,大小为View设定的数值
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }
    

    可以看到:DecorView的MeasureSpec值是由窗口大小及其布局参数来决定的。

    来张表格总结:

    布局参数 LayoutParams.MATCH_PARENT LayoutParams.WRAP_CONTENT 准确数值
    MeasureSpec值 EXACTLY windowSize(窗口大小) AT_MOST windowSize(不能超过窗口大小) EXACTLY rootDimension(准确数值)

    3.4.2 确定普通View的MeasureSpec值

    普通View的MeasureSpec值通过getChildMeasureSpec()方法来获取,getChildMeasureSpec()位于ViewGroup中:

    3.4.2.1 ViewGroup的getChildMeasureSpec

    /**
     *
     * @param spec 父容器的测量规格(MeasureSpec)
     * @param padding 子view的内边距和外边距(padding,margin) 
     * @param childDimension 子view的布局参数(宽/高)
     * @return 子view的测量规格(MeasureSpec)
     */
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        //父容器的测量模式
        int specMode = MeasureSpec.getMode(spec);
        //父容器的测量大小
        int specSize = MeasureSpec.getSize(spec);
    
        //子view大小 = 父容器大小-子view边距(子view只在某些地方地方用到这个值,具体看下面的代码)
        int size = Math.max(0, specSize - padding);
    
        //初始化子view的大小和模式(最终的结果需要下面代码计算出来) 
        int resultSize = 0;
        int resultMode = 0;
    
        switch (specMode) {
    
        case MeasureSpec.EXACTLY://当父容器模式为EXACTLY时
            if (childDimension >= 0) {// 当子view的LayoutParams>0,即有确切的值
                //子view大小为子自身所赋的值,模式大小为EXACTLY
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {// 当子view的LayoutParams为MATCH_PARENT时(-1)
                //子view大小为父容器剩余大小,模式为EXACTLY
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {// 当子view的LayoutParams为WRAP_CONTENT时(-2)
                //子view决定自己的大小,但最大不能超过父容器剩余大小,模式为AT_MOST
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
    
        case MeasureSpec.AT_MOST: // 当父容器的模式为AT_MOST时
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
    
    
        case MeasureSpec.UNSPECIFIED:// 当父容器的模式为UNSPECIFIED时
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                //这里的sUseZeroUnspecifiedMeasureSpec值为targetSdkVersion < Build.VERSION_CODES.M;即<23为true,否则为false.
                //大于等于23时会将size作为提示值,否则为0
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
    
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
    

    可以看到:普通View的MeasureSpec值是由父容器的MeasureSpec及其布局参数来决定的。

    来张表格总结:

    父容器测量模式(右侧)———————————-— 子View布局参数(下侧) EXACTLY(精确) AT_MOST(最多) UNSPECIFIED(未指定)
    具体dp/px EXACTLY childDimension(子View尺寸) EXACTLY childDimension(子View尺寸) EXACTLY childDimension(子View尺寸)
    MATCH_PARENT EXACTLY parentSize(父容器剩余大小) AT_MOST parentSize(不能超过父容器剩余大小) UNSPECIFIED targetSdkVersion<23 ? 0 : size; (大于等于23时会将size作为提示值)
    WRAP_CONTENT AT_MOST parentSize(不能超过父容器剩余大小) AT_MOST parentSize(不能超过父容器剩余大小)) UNSPECIFIED targetSdkVersion<23 ? 0 : size; (大于等于23时会将size作为提示值)

    对上表作一些规律总结:
    •以子View布局参数为标准,横向观察:

    1.当子View使用具体的dp/px时,无论父容器的测量模式是什么,子View的测量模式都是EXACTLY且大小等于设置的具体数值;
    2.当子View使用match_parent时,子View的测量模式与父容器的测量模式保存一致。

    另外,UNSPECIFIED模式一般很少用到,可以不用过多关注。

    1. measure过程分析

    measure过程根据View的类型可以分为两种情况:

    1. 测量单一View时,只需测量自身;
    2. 测量ViewGroup时,需要对ViewGroup中包含的所有子View都进行测量。

    我们对这两种情况分别进行分析。

    4.1 单一View的measure过程

    单一View的measure过程由View的measure()来实现:

    4.1.1 View的measure

    /**
     *
     * @param widthMeasureSpec view的宽测量规格
     * @param heightMeasureSpec view的高测量规格
     */
     public final void measure(int widthMeasureSpec, int   heightMeasureSpec) {
        //...
        onMeasure(widthMeasureSpec, heightMeasureSpec);
        //...  }
    

    这里传入的widthMeasureSpec/heightMeasureSpec是当前View的测量规格,通过getChildMeasureSpec()来获得的。具体细节可以看上面的分析。
    measure()方法为final类型,子类不能对其进行重写。measure()方法中主要就是对基本测量逻辑作判断,最终会调用onMeasure()方法。

    4.1.2 View的onMeasure

       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
    

    上面就一行代码,看起来很简单,但是包含了三个方法:getSuggestedMinimumWidth()/getSuggestedMinimumHeight()、getDefaultSize()、setMeasuredDimension(),我们分开来看:

    4.1.3 View的getSuggestedMinimumWidth

    getSuggestedMinimumHeight()与getSuggestedMinimumWidth()同理,我们这里只看下getSuggestedMinimumWidth():

    protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }
    

    如果View没有设置背景,那么View的宽度为mMinWidth,而mMinWidth就是android:minWidth属性所指定的值,若android:minWidth没有指定,则默认为0;
    如果View设置了背景,那么View的宽度为mMinWidth和mBackground.getMinimumWidth()中的最大值。

    那么mBackground.getMinimumWidth()的值是多少呢,我们来看看:

    4.1.4 Drawable的getMinimumWidth

    public int getMinimumWidth() {
        final int intrinsicWidth = getIntrinsicWidth();//返回背景图Drawable的原始宽度
        return intrinsicWidth > 0 ? intrinsicWidth : 0;
    }
    

    可以看出:mBackground.getMinimumWidth()返回的是背景图Drawable的原始宽度,若无原始宽度则返回0。

    那么Drawable什么情况下有原始宽度?如:ShapeDrawable没有,但BitmapDrawable有。

    4.1.5 View的getDefaultSize

    我们再来看看View的getDefaultSize()方法:

    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);//获取测量模式
        int specSize = MeasureSpec.getSize(measureSpec);//获取测量大小
    
        switch (specMode) {
        //测量模式为UNSPECIFIED时,View的测量大小为默认大小,即getSuggestedMinimumWidth()/getSuggestedMinimumHeight()返回的结果。
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        //测量模式为AT_MOST、EXACTLY时,View的测量大小为测量规格中的测量大小
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
    

    4.1.6 View的setMeasuredDimension

    再来看setMeasuredDimension():

    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
        //...
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }
    

    setMeasuredDimension()里会调用setMeasuredDimensionRaw():

    4.1.7 View的setMeasuredDimensionRaw

    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;
    
        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }
    

    setMeasuredDimensionRaw()中就是对测量出的宽高进行保存。
    到这里,单一View的measure过程就完成了。

    4.1.8 时序图

    最后,来张相关的时序图:


    单一View测量过程时序图

    4.1.9 流程图

    以及来张测量过程细节流程图:


    单一View测量过程流程图

    4.2 ViewGroup的measure过程

    由于ViewGroup包含了子View,因此其measure过程是通过遍历所有的子View并对子View测量,然后合并所有子View的尺寸,最后得到ViewGroup的测量值。

    ViewGroup测量顺序图

    如上图所示,要执行ViewGroup的measure,首先从顶部的ViewGroup开始遍历,自上而下递归执行子View的measure。

    ViewGroup的measure过程是从measureChildren()开始的。

    4.2.1 ViewGroup的measureChildren

    /**
     *
     * @param widthMeasureSpec ViewGroup的宽测量规格
     * @param heightMeasureSpec ViewGroup的高测量规格
     */
    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {//遍历子View
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
    

    再来看measureChild():

    4.2.2 ViewGroup的measureChild

    protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();//获得子view的布局参数
    
        //获得子view的宽/高测量规格
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);
    
        //子view开始测量
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    

    4.2.3 View的measure

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    //...
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    //...
    }
    这里的measure跟上面单一View的measure方法是一样的。

    4.2.4 ViewGroup的onMeasure

    如果子View是一个单一的View的话,则直接调用View的onMeasure()方法,跟上面单一View的measure过程是一样大的;
    如果子View是ViewGroup的话,理论上应该是应该调用ViewGroup的onMeasure()方法的。
    但实际上,ViewGroup中是没有onMeasure()这个方法的,ViewGroup作为一个抽象类,需要其子类去实现测量过程的onMeasure()方法,比如LinearLayout、RelativeLayout等。因为LinearLayout、RelativeLayout这些布局具体不同的特性,其测量细节各有不同,无法进行统一的实现,因此需要其子类去进行具体的实现。因此我们自定义ViewGroup时需要重写onMeasure()方法。
    我们这里看下LinearLayout的onMeasure()实现:

    4.2.5 LinearLayout的onMeasure

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {//方向判断
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }
    

    LinearLayout会区分方向来进行不同的测量方法,我们主要看下竖向的测量measureVertical(),横向的原理差不多这里就不看了。

    4.2.6 LinearLayout的measureVertical

    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    
        //获取子view数量
        final int count = getVirtualChildCount();
    
        //获取LinearLayout的宽/高测量模式
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    
        //...
    
        for (int i = 0; i < count; ++i) {//遍历子View
            final View child = getVirtualChildAt(i);
    
            //..
    
            // 最终会调用子View的measure(),计算出子View的大小
            measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                        heightMeasureSpec, usedHeight);
    
            // 获取子View的测量高度
            final int childHeight = child.getMeasuredHeight();
    
            //..
    
            final int totalLength = mTotalLength;
    
            // 将子View的测量高度以及Margin加到LinearLayout的总高度上
            mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                       lp.bottomMargin + getNextLocationOffset(child));
        }
    
        //..
    
        // 加上LinearLayout设置的Padding
        mTotalLength += mPaddingTop + mPaddingBottom;
    
        int heightSize = mTotalLength;
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
    
        //..
    
        //保存测量值
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),heightSizeAndState);
    
    
    }
    

    最后会调用setMeasuredDimension()来保存测量好的值,至此ViewGroup的测量过程就完成了。

    4.2.7 时序图

    最后,来张相关的时序图:

    ViewGroup测量过程时序图

    4.2.8 流程图

    以及来张ViewGroup测量过程细节流程图:

    ViewGroup测量过程流程图
    1. 自定义View

    5.1 自定义单一View

    自定义单一View,可以直接使用View中默认定义的onMeasure()方法。如果有时需要更精准的测量,可以重写onMeasure()。

    5.2 自定义ViewGroup

    由于ViewGroup没实现onMeasure(),所以自定义ViewGroup需要重写onMeasure()方法。这里给个简单的模板:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
        //定义存放测量后的View的宽和高的变量
        int widthMeasure;
        int heightMeasure;
    
    
        //实现测量方法以及具体的测量细节
        measureMethod();
    
        //必不可少  保存测量后View宽和高的值
        setMeasuredDimension(widthMeasure, heightMeasure);
    }
    
    1. 其他问题

    6.1 获取View的测量宽高

    我们可以使用getMeasuredWidth()和getMeasuredHeight()来获取View测量出的宽高。

    但是从上面的代码分析可以看到,在调用setMeasuredDimension()方法之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取View测量出的宽高,在这之前去调用这两个方法得到的值都会是0。

    结合Activity的启动过程以及生命周期,在onCreate()和onResume()中,都还未开始进行测量的操作,所以这时候调用getMeasuredWidth()和getMeasuredHeight()的值都会是0。

    另外,在某些情况下,需要多次测量才能确定View最终宽高;因此这种情况下获得的值可能是不准确的,建议在layout过程中onLayout()去获取最终宽高。

    6.2 自定义View时WRAP_CONTENT不生效的问题

    从上面getDefaultSize()方法的代码可以看到,无论是AT_MOST模式还是EXACTLY模式,View的测量大小都为测量规格中的测量大小,所以我们就会看到自定义View使用WRAP_CONTENT会起到跟MATCH_PARENT的效果。

    为了解决这个问题需要在LayoutParams属性为WRAP_CONTENT时指定一下默认的宽和高,如:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
        int width=100;//设置一个默认宽
        int height=100;//设置一个默认高
    
        if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(width, height);//宽高都为WRAP_CONTENT都设置为默认宽高
        } else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(width, heightSize);//只有宽为WRAP_CONTENT时,只设置默认宽
        } else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(widthSize, height);//只有高为WRAP_CONTENT时,只设置默认高
        }
    }这里的默认值请根据实际情况去确认。 
    

    像系统的TextView这些都支持WRAP_CONTENT,可以去看下其onMeasure()的源码实现。
    如果我们自定义的View只需要设置指定的具体宽高或者MATCH_PARENT,可以不用重写onMeasure()方法。

    相关文章

      网友评论

      本文标题:View篇之Measure过程

      本文链接:https://www.haomeiwen.com/subject/wiiaxftx.html