Android View工作机制(2)—measure过程 上

来源:互联网 发布:炫酷证书单页源码 编辑:程序博客网 时间:2024/06/05 03:35
为了理解View的测量过程,我们先要了解MeasureSpec。从字面意思看MeasureSpec是测量规格的意思。在Android中,MeasureSpec是一个32位的int值,高2位代表specMode,即测量模式,后30位代表品SpecSize,为某种测量模式下的规格大小。SpecMode有三种,分别为:1. UNSPECIFIED    父容器对View没有限制,View想多大就多大2. EXACTLY    父容器已经检测出View所需要的精确大小,此时View的大小就是SpaceSize。它对应于LayoutParams中的match_parent。3. AT_MOST父容器指定了一个大小,即SpaceSize,View的大小不能大于这个值。它对应了LayoutParams中的wrap_content。由此规则,我们可以对View的MeasureSpace的创建规则进行一个归纳。(参考自《Android开发艺术探索》)

参考自《Android开发艺术探索》

View的measure过程是一个final方法,所以子类不能重写这个方法。View的方法会调用View的onMeasure方法。onMeasure方法的源码如下
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }
在onMeasure方法中,通过setMeasureDimension方法设置View的宽高。下面我们来看一下getDefaultSize方法的实现。
public static int getDefaultSize(int size, int measureSpec) {        int result = size;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        switch (specMode) {        case MeasureSpec.UNSPECIFIED:            result = size;            break;        case MeasureSpec.AT_MOST:        case MeasureSpec.EXACTLY:            result = specSize;            break;        }        return result;    }
可以发现,在这个方法中获取了SpaceSize和SpaceMode,根据SpaceMode的不同进行不同的设置。如果是UNSPECIFIED模式,则结果的大小就参数中的size,否则是获取到的SpaceSize。而getDefaultSize方法中的宽高获取的方法为getSuggestedMinimumWidth,此方法的实现为
protected int getSuggestedMinimumWidth() {        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());    }
从此段代码可以看出,如果View没有设置背景,那么他的宽度为mMinWidth,否则为mMinWidth和背景大小中比较大的那一个。那么背景的Drawable.getMinImunWidth方法获取的是什么呢?查看此方法的源码
public int getMinimumHeight() {        final int intrinsicHeight = getIntrinsicHeight();        return intrinsicHeight > 0 ? intrinsicHeight : 0;    }
这段代码的含义是,获取这个背景Drawable的初始高度,如果高度大于0,则返回背景的高度,否则返回0。从getDefaultSize的实现来看,View的宽高是由SpaceSize决定的。为了使用View的wrap_content属性,我们在自定义View的时候,需要重写View的onMeasure方法。编写的方法可以模仿系统自己内部测量的getDefaultSize方法。下面我们来举个例子:新建一个类,为MyView.java,继承自View实现View的onMeasure方法代码如下:
@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));    }
编写方法measureWidth和measureHeight,测量View的宽高,这里我们假定View最大为200px。则measureWidth方法的实现如下:
private int measureWidth(int measureSpace) {        int result = 0;        int spaceMode = MeasureSpec.getMode(measureSpace);  //获取测量模式        int spaceSize = MeasureSpec.getSize(measureSpace);  //获取测量的大小        if (spaceMode == MeasureSpec.EXACTLY) {            result = spaceSize;        } else {            result = 200;  //最大为200            if (spaceMode == MeasureSpec.AT_MOST) {                //如果是AT_MOST模式,则将宽度设置为200和spaceSize中比较小的那个                result = Math.min(result, spaceSize);            }        }        return result;    }
measureHeight的实现类似于measureWidth方法。我们着重讲解下AT_MOST模式下的那部分代码。如果检测到AT_MOST模式,那么我们先假定宽为最大值200。再将200与spaceSize比较。获取最小的那个。这样,我们在指定了wrap_content的时候,View的大小就为200。如果我们指定了比200还大的宽度,那么View仍然会把高度限制在200px,如果小于200,那么执行的是EXACTLY模式下的了逻辑,宽度就为我们设置的宽度,即spaceSize。我们在布局文件中放置自定义的View
<MyView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@android:color/holo_red_dark" />

运行效果如下所示
这里写图片描述

此时可以看出,我们将宽高设置为wrap_content的时候,View的大小均为200px。设置为其他大小的情况读者可以自行实验。

0 0
原创粉丝点击