解析View中的MeasureSpec

来源:互联网 发布:淘宝做外宣技巧 编辑:程序博客网 时间:2024/05/18 02:01

MeasureSpec是View中的一个内部类,封装了父View传给子View的布局需求:

 public static class MeasureSpec {        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);            }        }        public static int getMode(int measureSpec) {            return (measureSpec & MODE_MASK);        }        public static int getSize(int measureSpec) {            return (measureSpec & ~MODE_MASK);        }        static int adjust(int measureSpec, int delta) {            final int mode = getMode(measureSpec);            if (mode == UNSPECIFIED) {                // No need to adjust size for UNSPECIFIED mode.                return makeMeasureSpec(0, UNSPECIFIED);            }            int size = getSize(measureSpec) + delta;            if (size < 0) {                Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +                        ") spec: " + toString(measureSpec) + " delta: " + delta);                size = 0;            }            return makeMeasureSpec(size, mode);        }        public static String toString(int measureSpec) {            int mode = getMode(measureSpec);            int size = getSize(measureSpec);            StringBuilder sb = new StringBuilder("MeasureSpec: ");            if (mode == UNSPECIFIED)                sb.append("UNSPECIFIED ");            else if (mode == EXACTLY)                sb.append("EXACTLY ");            else if (mode == AT_MOST)                sb.append("AT_MOST ");            else                sb.append(mode).append(" ");            sb.append(size);            return sb.toString();        }    }

MeasureSpec定义了三种模式:
UNSPECIFIED:父View对子View没有限制,子View想多大就多大。一般这种模式很少用。
EXACTLY:父View可以确定子View的大小。
AT_MOST:子View不能超过父View限定的最大值。
在View的measure方法里面,会接收两个int参数:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {    ... }

这里面传入的值,不仅仅是代表View的size,还包含了MeasureSpec中的mode。在调用View的measure方法之前,必须先计算出View宽度的MeasureSpec和高度的MeasureSpec,最先调用measure方法的是DecorView,看看它是怎么计算MeasureSpec的吧:

 private static int getRootMeasureSpec(int windowSize, int rootDimension) {        int measureSpec;        switch (rootDimension) {        case ViewGroup.LayoutParams.MATCH_PARENT:            // Window can't resize. Force root view to be windowSize.            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);            break;        case ViewGroup.LayoutParams.WRAP_CONTENT:            // Window can resize. Set max size for root view.            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);            break;        default:            // Window wants to be an exact size. Force root view to be that size.            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);            break;        }        return measureSpec;    }

从上面的代码可以看到,根据不同的LayoutParams,调用makeMeasureSpec时传入不同的MeasureSpec mode,这也很好理解,LayoutParams决定者View的大小。关键就是makeMeasureSpec方法了:

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

这个方法很简单,但是理解起来并不容易。先把MeasureSpec 定义的几个常量用16进制表示:

0x3左移30位: MODE_MASK   = 11   00 0000 0000 0000 0000 0000 0000 000000 左移30位: UNSPECIFIED = 00   00 0000 0000 0000 0000 0000 0000 000001 左移30位:EXACTLY     = 01   00 0000 0000 0000 0000 0000 0000 000010 左移30位:AT_MOST     = 10   00 0000 0000 0000 0000 0000 0000 0000MODE_MASK取反:~MODE_MASK= 00   11 1111 1111 1111 1111 1111 1111 1111

size & ~MODE_MASK就是取size 的后30位,mode & MODE_MASK就是取mode的前两位,最后执行或运算,得出来的数字,前面2位包含代表mode,后面30位代表size。在measure方法里面需要取出size或者是mode时,可以调用下面两个方法:

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

这样做其实就是用一个int存储了两个信息,好处是节省内存开支,减少传参的个数,View源码中很多用这种方法来存储信息,比如View 的VISIBLE,ENABLED等等,这些View的状态,都是存储在int 变量mViewFlags之中,它里面不同的位数代表着不同的状态。
在View的measure过程中,我们知道子View的MeasureSpec和父View的MeasureSpec有有关联,计算子View的MeasureSpec是调用ViewGroup中的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 = 0;                resultMode = MeasureSpec.UNSPECIFIED;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size.... find out how                // big it should be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            }            break;        }        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }

从这个方法里面,可以用一个表格来说明父View MeasureSpec和子View size之间的联系:
这里写图片描述

0 0
原创粉丝点击