手动测量 View 的宽高

简单说明

手动调用 View 的 measure(int widthMeasureSpec,int heightMeasureSpec) 方法来得到 View 的宽高。

根据 View 的 LayoutParams 以下几种情况

具体数值(dp/px)

举个栗子,宽高都是 100 px,这时候,有我们来手动拼装合适的 MeasureSpec:

/**
   * 手动测量 View 的宽高,当 View 的宽高是精确值时
   *
   * @param target 需要测量的 View
   * @param widthSize 宽度
   * @param heightSize 高度
   */
  public void meaureManually(View target, int widthSize, int heightSize) {
    int widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
    int heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
    target.measure(widthMeasureSpec, heightMeasureSpec);
  }

wrap_content

/**
   * 手动测量 View 的宽高,当 View 的宽高是 wrap_content 时
   *
   * @param target 需要测量的View
   */
  public void measueManually(View target) {
    int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST);
    int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST);
    target.measure(widthMeasureSpec, heightMeasureSpec);
  }

这里有点儿小技巧,我将本来应该传入的 widthSizeheightSize 改为了 (1<<30)-1 ,看过 MeasureSpec 的源码就可以知道,这个特殊的 int 值就是 View 理论上能支持的最大值。

View 的尺寸使用 30 位二进制来表示,也就是说最大是 30 个 1(即 2^30 -1),也就是 (1<<30)-1。

match_parent

上一段 ViewGroup 的源码:

/**
     * Does the hard part of measureChildren: figuring out the MeasureSpec to
     * pass to a particular child. This method figures out the right MeasureSpec
     * for one dimension (height or width) of one child view.
     *
     * The goal is to combine information from our MeasureSpec with the
     * LayoutParams of the child to get the best possible results. For example,
     * if the this view knows its size (because its MeasureSpec has a mode of
     * EXACTLY), and the child has indicated in its LayoutParams that it wants
     * to be the same size as the parent, the parent should ask the child to
     * layout given an exact size.
     *
     * @param spec The requirements for this view
     * @param padding The padding of this view for the current dimension and
     *        margins, if applicable
     * @param childDimension How big the child wants to be in the current
     *        dimension
     * @return a MeasureSpec integer for the child
     */
    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.
                // 如果换我们来构造此时的 MeasureSpec,我们需要知道这个 size 的大小,然而并不能,所以放弃
                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.
                // 如果换我们来构造此时的 MeasureSpec,我们需要知道这个 size 的大小,然而并不能,所以放弃
                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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

发现如果要构造在 View 的宽高为 match_parent 时的 MeasureSpec,需要知道 parentSize,而此时因为无法知道 parentSize,所以直接放弃

NOTE

注意网上的一些奇怪代码,还是举两个常见的栗子:

栗子一

view.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

栗子二

int widthMeasureSpec = MeasureSpec.makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec, heightMeasureSpec);

第二个栗子最搞,现在在 Android Studio 里这么写编译器会直接给出错误提示,但是我以前经常能看到这样的代码(不明白原理的时候,我以前也这么做2333

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android相关

LinearLayout.onMeasure-声明变量

mTotalLength:表示所有子View所需要的高度 maxWidth:表示这个LinearLayout的宽度,最后设置宽度的时候用到的 childSt...

12820
来自专栏Android常用基础

自定义View(八)-View的工作原理- View的measure

从上一篇中。同Activity的布局加载了解了整个View树加载的流程。最后是通过View的三大流程来实现布局的显示的。那么我们这篇来讲下布局的三大流程之一--...

16810
来自专栏Android干货

自定义控件:数独游戏(一)

41680
来自专栏Android知识点总结

Android自定义控件之局部图片放大镜--BiggerView

21720
来自专栏Android常用基础

自定义View(九)-View的工作原理- View的layout()和draw()

上一节我们将View的测量流程理的差不多了,这篇我们来看下View的剩下的2大流程layout(布局)和draw(绘制)。相对测量来说,布局与绘制就简单了许多,...

12620
来自专栏Android源码框架分析

Android自定义View:MeasureSpec的真正意义与View大小控制

自定义View是Android开发中最普通的需求,灵活控制View的尺寸是开发者面临的第一个问题,比如,为什么明明使用的是WRAP_CONTENT却跟MATCH...

14820
来自专栏向治洪

自定义圆角和园边的实现

本来想在网上找个圆角的例子看一看,不尽人意啊,基本都是官方的Demo的那张原理图,稍后会贴出。于是自己自定义了个View,实现图片的圆角以及圆形效果。效果图: ...

22370
来自专栏肖蕾的博客

Android自定义控件之圆形进度条Android自定义控件之-圆形进度条

1.7K50
来自专栏Android知识点总结

Android关于Paint你所知道的和不知道的一切

29120
来自专栏项勇

笔记51 | Android自定义View(二)

19660

扫码关注云+社区

领取腾讯云代金券