View绘制学习之一——onMeasure

View绘制必定会调用的三个方法:
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "onMeasure: ");
    }

    @Override
    public void layout(@Px int l, @Px int t, @Px int r, @Px int b) {
        super.layout(l, t, r, b);
        Log.d(TAG, "layout: ");
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        Log.d(TAG, "draw: ");
    }
看看源码这三个方法写了什么
首先看看onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
源码中好像是又调用了setMeasuredDimension这个方法
然后参数是getDefaultSize(int size,int mesureSpec)
方法功能:
返回一个默认的size:宽度或者是高度。如果测量规范中没有对子view的大小进行限制的话,子view的大小使用该返回值。也有可能返回更大的值
public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        
        //获取到测量规范对象中的size和mode
       int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:

           //如果mode属于UNSPECIFIED即父view未对子view的大小做任何要求,则将默认的size返回给view
          result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
          
          //父view对子view的测量进行了限制,则所返回的值就是测量规范中size的值,
           //即父veiw给子view规定的值
          result = specSize;
            break;
        }
        return result;
    }
getSuggestedMinimumWidth()
protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }
mMinWidth:view的最小宽度
getMinimumWidth():drawable的最小宽度
方法功能:
该方法返回的是所建议的view应该使用的最小宽度(在view的最小宽度和背景的最小宽度两个值中取最大值返回)

widthMeasureSpec宽度测量规范:父view给子view规定的水平方向上的测量规范,测量规范会包含一个mode和一个size
1. 精确模式(MeasureSpec.EXACTLY)

在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。

EX:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。

2.最大模式(MeasureSpec.AT_MOST)

这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。

EX:当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。

3.未指定模式(MeasureSpec.UNSPECIFIED

这个就是说,当前组件,可以随便用空间,不受限制。这种情况不多
注意这里的widthMeasureSpec是个int型,那么如何来表示一个mode和一个size呢?
看下面的图
最高两位是00的时候表示"未指定模式"。即MeasureSpec.UNSPECIFIED
最高两位是01的时候表示"'精确模式"。即MeasureSpec.EXACTLY
最高两位是11的时候表示"最大模式"。即MeasureSpec.AT_MOST

这里android提供了一个工具类 MeasureSpec 工具类
这个工具类有四个方法和三个常量(上面所示)供我们使用:

 

//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。

public static int makeMeasureSpec(int size, int mode)

 

//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。

public static int getMode(int measureSpec)

 

//这个是得到这个变量中表示的尺寸大小的值。

public static int getSize(int measureSpec)

 把

//把这个变量里面的模式和大小组成字符串返回来,方便打日志

 public static String toString(int measureSpec)


getDefaultSize方法总结
也就是说,该方法返回了view的默认大小的值,这个值跟父view对子view是否进行了限制有关,
如果父view对子view没进行限制,则返回所建议的view的大小,
若进行了限制,则返回测量规范对象中的size。


最后回到setMeasuredDimension方法

方法介绍:
在调用onMeasure方法时必须调用该方法,来保存view所测量的宽和高,如果调用失败则会触发异常

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int opticalWidth  = insets.left + insets.right;
            int opticalHeight = insets.top  + insets.bottom;

            measuredWidth  += optical ? opticalWidth  : -opticalWidth;
            measuredHeight += optical ? opticalHeight : -opticalHeight;
        }
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }

方法参数:
measuredWidth:view的测量的宽
measuredHeight:view的测量的高

这里有调用了一个方法 setMeasuredDimensionRaw(int measuredWidth, int measuredHeight)

作用就是保存所测量的宽和高的值,并且设置标志位

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }

总结

1>, onMeasure方法用于测量view以及其内容的宽高 得到一组宽和高的值measurewidth/heigh,在调用measure方法时会调用(measure方法属于view的public方法),View的子类应该覆写onMeasure方法来提供一组准确有效的测量值。

2>,约定:在覆写onMeasure方法时必须调用setMeasuredDimension方法来存储所测量的宽高值,如果存储失败会触发measure抛出 的illegalStateException异常,调用父类的onMesure方法也可以避免这个异常。(笔者注:也就是说要么调用父类的onMesure方法,要么自己手动在子view的onMesure方法中调用setMearsuredDimension方法,否则会抛出异常)

3>,如果在测量规范中没有规定更大的值那么基类中的测量值默认是background的大小,建议view的子类覆写onMeasure方法来进行更好的测量、

4>,如果子类覆写了该方法,那么测量view大小的任务就交给子类了,所测量的宽高不能小于view本身提供了一组view的最小值,可以通过getSuggestedMinimumHeight()/width()获取,获取到的是一组默认的没有padding的一组wrap_content的宽高

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值