Android measure方法详解
发布日期:2021-05-14 01:37:44 浏览次数:12 分类:精选文章

本文共 4534 字,大约阅读时间需要 15 分钟。

MeasureSpec类及其在Android测量中的应用

Android中的MeasureSpec类是View系统中用于测量子视图尺寸的关键机制,主要用于确定子视图的宽度和高度。MeasureSpec提供三种测量模式:UNSPECIFIED、EXACTLY和AT_MOST。了解这些模式的工作原理,对于优化Android布局 layouts 是非常重要的。


MeasureSpec类概述

MeasureSpec类用于描述一个控件的尺寸限制,主要分为三种模式:

  • UNSPECIFIED(未定义):表示尺寸没有被明确限制,可以为0或任意值。

  • EXACTLY(准确):表示尺寸被明确指定为一个特定的值。

  • AT_MOST(最多):表示尺寸不超过某个最大值,子控件可以根据需要选择适当的尺寸。

  • 获取MeasureSpec的模式和尺寸

    通过静态方法getMode和getSize,可以获取MeasureSpec的当前模式和尺寸值:

    public static int getMode(int measureSpec);public static int getSize(int measureSpec);

    View的测量方法

    每个View都需要通过measure方法(measure(int, int))来计算它的宽度和高度。这通常包括以下步骤:

  • 获取测量器(MeasureSpec):测量器由父控件提供,包含宽度和高度的限制。

  • 执行或缓存测量:如果有forceLayout布局强制刷新,会立即进行测量;否则会尝试先从缓存中获取。

  • 计算尺寸:通过onMeasure方法或setMeasuredDimensionRaw方法设置测量结果。

  • 具体实现

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {    // 如果有布局强制刷新,立即测量,否则尝试从缓存中获取结果    int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);    if (cacheIndex < 0 || sIgnoreMeasureCache) {        // urch herself measured,设置测量结果        onMeasure(widthMeasureSpec, heightMeasureSpec);        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;    } else {        // 从缓存中获取测量结果        long value = mMeasureCache.valueAt(cacheIndex);        // 可以通过位运算忽略高32位,直接设置尺寸        setMeasuredDimensionRaw((int)(value >> 32), (int)value);        mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;    }}

    ViewGroup的测量Child方法

    ViewGroup不仅需要测量自身尺寸,还需要处理子控件的测量。系统提供了以下方法来实现这一功能:

  • measureChildren(int, int):遍历所有直接子控件,只有View.GONE以外的控件会被测量。

  • measureChild(View, int, int):为单个子控件创建MeasureSpec,并调用其measure方法。

  • 具体实现

    protected void measureChild(View child, int parentWidthMeasureSpec,    int parentHeightMeasureSpec) {    // 通过getChildMeasureSpec方法为子控件创建MeasureSpec    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方法

    getChildMeasureSpec方法根据父控件的MeasureSpec和子控件的布局属性(如MATCH_PARENT、WRAP_CONTENT)来确定子控件的尺寸。

    具体实现

    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {    int specMode = MeasureSpec.getMode(spec);    int specSize = MeasureSpec.getSize(spec);    // 根据specMode判断子控件尺寸的限制    switch (specMode) {        case EXACTLY: {            if (childDimension >= 0) {                // 指定为具体值                return MeasureSpec.makeMeasureSpec(childDimension, EXACTLY);            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // 子控件尺寸与父控件一致                return MeasureSpec.makeMeasureSpec(specSize - padding, EXACTLY);            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // 子控件宽度由内容决定,不超过父控件尺寸                return MeasureSpec.makeMeasureSpec(specSize - padding, AT_MOST);            }            break;        case AT_MOST: {            if (childDimension >= 0) {                // 允许子控件指定尺寸                return MeasureSpec.makeMeasureSpec(childDimension, EXACTLY);            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // 子控件尺寸与父控件一致,但父控件尺寸不定                return MeasureSpec.makeMeasureSpec(specSize - padding, AT_MOST);            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // 子控件宽度由内容决定,不超过父控件尺寸                return MeasureSpec.makeMeasureSpec(specSize - padding, AT_MOST);            }            break;        case UNSPECIFIED: {            if (childDimension >= 0) {                // 允许子控件指定尺寸                return MeasureSpec.makeMeasureSpec(childDimension, EXACTLY);            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // 子控件尺寸与父控件一致                return MeasureSpec.makeMeasureSpec(specSize - padding, UNSPECIFIED);            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // 子控件宽度由内容决定                return MeasureSpec.makeMeasureSpec(specSize - padding, UNSPECIFIED);            }            break;        }    }    // 返回默认MeasureSpec    return childDimension == 0 ? MeasureSpec.UNSPECIFIED : MeasureSpec.makeMeasureSpec(childDimension, EPSILON);}

    相关文章

  • [Android布局测量原理深入解析](https://bytes.baro.com/_/articles/android-layout-measure principle)
  • [View和ViewGroup的测量机制](https://bytes.baro.com/_/articles/View-ViewGroup-measurement mechanism)
  • 通过理解MeasureSpec类和 τις ViewGroup测量方法,可以更好地优化Android布局,提升应用性能和用户体验。

    上一篇:Android ListView滚动条
    下一篇:Android 自定义流式布局

    发表评论

    最新留言

    能坚持,总会有不一样的收获!
    [***.219.124.196]2025年04月06日 16时09分22秒