Something about measure

来源:互联网 发布:c语言if条件语句 编辑:程序博客网 时间:2024/05/17 23:43
在Android开发中,与View相关的理解中有三个过程比较重要,也比较难理解,这里简单说一下与Measure相关的问题:

Measure的作用:
     Measure这个流程主要作用是将布局中诸如一些MATCH_PARENT、WRAP_CONTENT结合View自身的特点要求,获取View最终大小的这么一个过程。举一个简单例子,你一个手机宽为600像素,你的TextView和上层布局的layout_width都是MATCH_PARENT,那你最终的TextView会在横向上都充满屏幕,在这个Measure的流程中,TextView控件最终会设置宽度为600像素。在Measure的过程中有几个重要概念,先解释一下:

1:Measure Mode
Measure Mode主要有三类,其中主要用的是(1)、(2),他们基本的解释情况如下,至于它们如何发挥作用,与布局属性有何关联呢,后边会详细介绍:
(1)、EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
(2)、AT_MOST(至多),子元素至多达到指定大小的值。
(3)、UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
2:MeasureSpec

    大家如果看到源码或者看开源的一些自定义的View控件时,可能经常会在onMeasure()方法中看到类似的源码:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

//获取view长宽方向上的mode、mode类型如上边介绍

int  widthMode= MeasureSpec.getMode(widthMeasureSpec);

int heightMode == MeasureSpec.getMode(heightMeasureSpec);

//获取view长宽方向的长度

int width=  MeasureSpec.getSize(widthMeasureSpec);

int height=MeasureSpec.getSize(heightMeasureSpec);

.....

}

不知道到这里大家有没有疑问,为什么从widthMeasureSpec一个变量中,我们可以获取到mode和宽度两个值,不知道大家有没有这个疑问,反正我第一次看到这个代码时感觉有些怪异,对于这个问题这里先简单解释一下后边会结合源码进行详细阐述,widthMeasureSpec这个变量时一个32位的int,其中他高位的16位记录着mode、低位的16位则记录着具体的size,因此同一个值既可以获取mode也可以获取size。
3:mode与wrap_content、match_parent的关系
关于这个问题还是上源码比较清楚,在ViewRoot里有getRootMeasureSpec方法,方法实现如下:
 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;
    }

MeasureSpec.makeMeasureSpec()这个方法咱们下边马上可以说到,但是从源码可以看到当布局方式为WRAP_CONTENT时,mode为MeasureSpec.AT_MOST,当布局方式为MATCH_PARENT和具体的值时都为MeasureSpec.EXACTLY;
4:MeasureSpec源码
通过上边介绍我们会发现MeasureSpec类是一个重要的类,通过MeasureSpec可以获取一个控件的布局mode和size,布局文件中设置的WRAP_CONTENT、MATCH_PARENT等都会对应一个mode,接下来看看MeasureSpec的实现:
public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has not imposed any constraint
         * on the child. It can be whatever size it wants.
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has determined an exact size
         * for the child. The child is going to be given those bounds regardless
         * of how big it wants to be.
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * Measure specification mode: The child can be as large as it wants up
         * to the specified size.
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        /**
         * Creates a measure specification based on the supplied size and mode.
         *
         *
         * @param size the size of the measure specification
         * @param mode the mode of the measure specification
         * @return the measure specification based on size and mode
         */
        public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

        /**
         * Extracts the mode from the supplied measure specification.
         *
         * @param measureSpec the measure specification to extract the mode from
         */
        public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }

        /**
         * Extracts the size from the supplied measure specification.
         *
         * @param measureSpec the measure specification to extract the size from
         * @return the size in pixels defined in the supplied measure specification
         */
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }

        static int adjust(int measureSpec, int delta) {
            return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));
        }

        /**
         * Returns a String representation of the specified measure
         * specification.
         *
         * @param measureSpec the measure specification to convert to a String
         * @return a String with the following format: "MeasureSpec: MODE SIZE"
         */
        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();
        }
    }


通过源码我们可以看到makeMeasureSpec()方法,是将mode和size搞到一个int中了,同样getSize(),getMode()方法又可以将相应的size和mode进行还原
5:Tips
与measure有关的应用主要在自定义view上,主要在于如何去重写onMeasure方法,这里不在说为何需要去重写onMeasure方法,与measure具体相关的流程,网上有一堆相关博客,大家可以参考一下。在onMeasure方法的重写上又根据View和ViewGroup不同,实现方式不同,View的实现方式上主要根据onmeasure里传过来的参数获取mode和size,以及自身所需要的size进行计算合适的大小,如果是mode是exectly,则控件大小为传过来的size大小,如果mode是at_most,则控件大小为传过来的size和自身所需size的最小值即可,当然了,我们可以不用管传过来的mode和size是啥,给控件设置一个具体的大小,这样程序不会有任何问题,只是可能会在整体显示上没有那么美观。对于ViewGroup来说,在onMeasure里主要去测量子类的大小,这个根据自定义ViewGroup的不同可能实现会有差异。

0 0
原创粉丝点击