自定义控件

image.png

自己的一点理解:

其实measureSpec就是view的一个内部类,封装了这个view对象的Mode和Size,封装在一个Int类型中,4字节*8位=32位,前2位封装的mode,后30位封装的size

image.png

推荐的两篇博客地址:

  • https://www.jianshu.com/p/705a6cb6bfee
  • https://www.jianshu.com/p/c84693096e41

先说下结论,onMeasure是测量viewviewGroup时系统调用的,它的参数widthMeasureSpecheightMeasureSpec既不代表父ViewGroup的宽高也不代表子View的宽高,它表示了父ViewGroup对自己的期望,至于子View实际的宽高得看setMeasuredDimension传的是什么。

class MyView:View {
    constructor(context: Context,attrs:AttributeSet):super(context,attrs){}
    constructor(context: Context):super(context){}


    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var myWidth = 0
        var myHeight = 0
        var defaultValue = 50

        var widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var heightMode = MeasureSpec.getMode(heightMeasureSpec)
        var widthSize = MeasureSpec.getSize(widthMeasureSpec)
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        //如果两个的模式同时为EXACTlY
        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            myHeight = heightSize
            myWidth = widthSize
        } else {
            //如果有一个模式为 AT_MOST
            myWidth = defaultValue
            myHeight = defaultValue
        }
        setMeasuredDimension(myWidth, myHeight)
    }
}
  • 有一个为AT_MOST的时候


    image.png
  • 同时为EXACTLY得时候


    image.png

  • 自定义ViewGroup时,什么时候使用child.measure什么时候使用measureChild

  • 使用child.measure
    当不想使用xml中给出的值的时候或者说子View或者子ViewGroup(统称子控件)的大小需要自己写出、控制的时候。比如我想自己设置
    子控件宽为:childWidth --- 模式为:EXACLTLY
    子控件高为:childHeight --- 模式为:EXACTLY
        var childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth,MeasureSpec.EXACTLY)
        var childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight,MeasureSpec.EXACTLY)

自己make子控件的measureSpec,然后调用child的measure方法设置子控件的实际大小child.measure(childWidthSpec,childHeightSpec)


  • 使用measureChild
    当xml文件中已经指定了子控件的大小,并且我就像按照这个大小模式来设置我的子控件的实际大小measureChild(child, widthMeasureSpec, heightMeasureSpec)
    第一个参数是哪一个子控件,后面两个参数是父控件的宽高测量建议。
    源码
 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);
    }

而其中的getChildMeasureSpec()源码如下:

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
  • 调用measureChild方式实际上就是根据父容器的大小和模式以及自己的布局参数来确定子控件的measureSpec

  • 而调用child.meaure实际上就是不想依据系统默认的方式来确定子控件的measureSpec,而是自己根据自己的实际需求来手动调用measure.makeMeasureSpec()来确定子控件的measureSpec

由于时间仓促,很多东西只是泛泛而谈,有时间会将这些内容整理一遍

你可能感兴趣的:(自定义控件)