MeasureSpec理解

2021-04-25  本文已影响0人  Gxinyu

简要

  今天来聊聊MeasureSpec,记得刚接触的也感觉很难理解,知其然不知其所以然。MeasureSpec其实在面试中还经常会被问到,如果没有真正去理解它,不论是后续的开发或者面试中,难免会成为绊脚石。遂今天在此聊聊这个,可能以下文中理解有所偏颇。如果有什么不到之处或者看完还有不太理解的地方,可以在文章后面写下你的评论一起去探讨。
  一说到MeasureSpec,大家本能反应是其是由高两位的mode和低30位的size组成的32位的一个int值,但要是再深入问一些可能回答不出来,例如:MeasureSpec为什么由mode+size的方式组成?MeasureSpec的值跟什么有关?或者父View与子View的MeasureSpec值的关系?自定义Viewd的onMeasure()方法要不要重写?你还在为这些面试中经常问的问题为难到吗?相信看完下文,心里多多少少都会有个答案。

组成

  其实MeasureSpec是View的一个内部类,真正的身份就是帮助View完成测量功能。MeasureSpec类中最主要的部分由5个变量和3个方法组成,接下来根据原码中的代码慢慢一层层剖析。

变量

五个成员变量:

    private static final int MODE_SHIFT = 30;
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
    public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    public static final int EXACTLY     = 1 << MODE_SHIFT;
    public static final int AT_MOST     = 2 << MODE_SHIFT;
方法

三个方法

public static int makeMeasureSpec( int size,int mode) {
    if (sUseBrokenMakeMeasureSpec) {
          return size + mode;
     } else {
          return (size & ~MODE_MASK) | (mode & MODE_MASK);
     }
}

@MeasureSpecMode
public static int getMode(int measureSpec) {
 //noinspection ResourceType
  return (measureSpec & MODE_MASK);
}

public static int getSize(int measureSpec) {
  return (measureSpec & ~MODE_MASK);
}

举例:
MeasureSpec运算.png

位运算符号:
<< :左移位运算 符号左边是值,符号右边是左移的位数
& :与运算 相同位的值同为1则为,否为0
~ :优先级比算数运算符、关系运算符、逻辑运算符和其他运算符都高

解释

来源和决定因素

  上面的组成部分讲解了MeasureSpec内部结构,其中mode与size参数的意义和如何获取。接下来说一说其如何帮助测量View的。我们都知道整个View体系是树状结构的,测量是由上至下的过程,View的测量功能由onMeasure()和measure()方法完成,其中onMeasure()属于回调方法,其结果再一层层回溯给顶层View。其中开发中我们只需要关注onMeasure()方法。onMeasure()方法中最重要的参数就是MeasureSpec值,接下来看一下自定义View的onMeasure()方法。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        switch (specMode) {
            case MeasureSpec.EXACTLY:
                Log.e("TAG", "ChildView:MeasureSpec.EXACTLY");
                break;
            case MeasureSpec.AT_MOST:
                Log.e("TAG", "ChildView:MeasureSpec.AT_MOST");
                break;
            case MeasureSpec.UNSPECIFIED:
                Log.e("TAG", "ChildView:MeasureSpec.UNSPECIFIED");
                break;
        }
    }

说明:
本文以下内容均只针对宽方向上来说

MeasureSpec树状图.png

上图说明:
1、上图是自定义View没重写onMeasure()的情况,谨记!!!
2、childView括号中的三个值从左到右为:LayoutParams的值、MeasureSpec的mode值(已经确定的)、测量过后的自身size值(此时称为measuredWidth)
3、正常情况下UNSPECIFIED模式用不到,所以自定义View的时候可以不做处理
4、getMeasuredWidth()≠getWidth(),getMeasuredWidth()在测量之后就有值了,但是getWidth()是没有值的
5、只有自身mode为EXACTLY时,测量的size值为自己设置的,mode为AT_MOST的时候size全是父类的值

总结

  这里做一下总结,其中有两部分组成,第一部分是内部的组成关系,组成关系中最主要的是几个位运算和几个参数的意义。第二部分是值的来源和决定因素,onMeasure()方法中值由父类传递过来,其值是根据父类的mode和自身的LayoutParams决定。如果还感觉很模糊,不妨自己去写个例子去验证一下,这样可以加深印象,最重要的还是要理解。

上一篇下一篇

猜你喜欢

热点阅读