您的位置:

Android OnMeasure详解

Android OnMeasure是ViewGroup中用于确定视图大小的方法,它掌握了视图在屏幕上的高度和宽度,而这些信息又是布局和绘制所必需的。在这篇文章中,我将从以下几个方面进行详细阐述OnMeasure的用处与实现原理。

一、OnMeasure的作用

OnMeasure是Android布局中最为关键的方法之一,它在布局时确定视图的大小。当ViewGroup被测量时,每个子视图的OnMeasure方法都会被调用,ViewGroup就能在得到自己的即将使用的尺寸之后,调用子视图的OnLayout方法,以达到正确绘制的效果。

OnMeasure方法实际上是两个部分:测量模式和测量值。单独一个测量模式是没法确定测量大小(测量值)的。

MeasureSpec是Android中非常重要的一个类,它表示了一个ViewGroup上的子视图的布局要求。MeasureSpec是由一个模式值和大小组成的,仅仅知道模式是没有用的,需要同时知道大小。

MeasureSpec由高2位代表模式,低30位表示大小。模式定义如下:

  • UNSPECIFIED(不确定模式)–视图想要多大就可以多大
  • EXACTLY(精确模式)–视图必须恰好fill_parent,match_parent(-1dp)或者一个确定的大小
  • AT_MOST(至多模式)–视图可以是任何大小,但不能比fill_parent还要大

二、OnMeasure的实现方式

在编写自定义视图时,我们可以需要重写其OnMeasure方法。下面是一个自定义视图的例子:

public class MyView extends View {

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int desiredWidth = 100;
        int desiredHeight = 100;

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width;
        int height;

        //确定宽度大小
        if (widthMode == MeasureSpec.EXACTLY) {
            //父视图要求一个具体的大小
            width = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            //调整为目标尺寸或更小
            width = Math.min(desiredWidth, widthSize);
        } else {
            //unspecified
            width = desiredWidth;
        }

        //确定高度大小
        if (heightMode == MeasureSpec.EXACTLY) {
            //父视图要求一个具体的大小
            height = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            //调整为目标尺寸或更小
            height = Math.min(desiredHeight, heightSize);
        } else {
            //unspecified
            height = desiredHeight;
        }

        //重写后,调用此方法让ViewGroup知道子视图的大小
        setMeasuredDimension(width, height);
    }
}

这个例子中的MyView是一个非常简单的自定义View,它只是绘制了一些“虚假”内容,但是展示了如何实现OnMeasure方法。OnMeasure方法在这个例子中通过制定一个预期的大小和测量规则来获取大小和规则,具体实现如下:

  • 从MeasureSpec中解析出测量模式(mode)和测量值(size)
  • 如果mode是EXACTLY,尺寸就是size。这可能是父视图在布局时,为子视图指定精确的大小。
  • 如果mode是AT_MOST,尺寸不能大于size。这可能是父视图在布局时,应该根据子视图的大小来调整。可以使用整个屏幕空间,但不能比其指定大小还要大。
  • 如果mode是UNSPECIFIED,这意味着视图沿着父视图方向想要多大就能有多大。这种情况不太常见。例子就省略了此情况。

三、OnMeasure的使用场景

在很多情况下,OnMeasure需要进行重载。

  • 自定义视图的大小
  • 视图内部的子视图数目不固定
  • 视图内部有嵌套的自定义视图
  • 视图的大小和位置是由其他组件决定的

在这些情况中,视图根据其成为一个复杂或特定的类别而变得独特。那么我们就需要重写OnMeasure方法,以实现自己期望的测量方式的视图。下面的代码中,将演示如何在一个具体情况中重载OnMeasure方法,具体实现如下:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        assert (widthMode == MeasureSpec.UNSPECIFIED
                || widthMode == MeasureSpec.AT_MOST);
        assert (heightMode == MeasureSpec.UNSPECIFIED
                || heightMode == MeasureSpec.AT_MOST);

        int desiredWidth = 200;
        int desiredHeight = 200;

        int width = desiredWidth;
        int height = desiredHeight;

        setMeasuredDimension(width, height);
    }

在这个例子中,我们获取了ViewGroup的宽度和高度的测量模式,并计算出一个指定的大小,除此之外,我们也可以重写此方法来根据状态调整大小。

四、OnMeasure的注意事项

在重载OnMeasure方法时,需要注意以下几点:

  • 对于自定义ViewGroup,必须调用子视图的measure()方法来确定其大小
  • 尝试最大限度的让布局不受限制
  • 您可以根据需要以不同的方式调整视图的大小,但必须要注意在大多数情况下,覆盖View的onMeasure()不是一个好的设计模式
  • 考虑OnLayout的实现,正确定位是在OnLayout中处理的,而不是在OnMeasure中

五、总结

Android OnMeasure是实现自定义视图重要的方法,实现时需要注意一些细节。它通过MeasureSpec抽象类来规定测量大小和模式,以及调整ViewGroup子元素的大小。

以上是关于OnMeasure的详细介绍,希望读者能够了解布局视图的最基本方法。完成这篇文章,你已经掌握了如何利用OnMeasure打造自己的自定义视图,同时也学习了一些重要的注意事项。