onMeasure():wrap_content,我怎么知道要包装的大小?

时间:2012-11-07 16:22:33

标签: android view custom-component

我创建了一个自定义视图,其中onDraw()被覆盖,在画布上绘制一个位图。当我在布局文件中指定我想要它wrap_content时,它仍然会填满整个屏幕。 onMeasure()说:

  

measure的基类实现默认为背景大小,除非MeasureSpec允许更大的大小。子类应覆盖onMeasure(int,int)以提供更好的内容测量。

好酷,所以我知道我需要覆盖onMeasure()并使用MeasureSpec。根据{{​​3}}

  

UNSPECIFIED表示layout_width或layout_height值设置为wrap_content。你可以任意大小。

现在我遇到了我的问题,我如何在onMeasure()测量我尚未创建的位图并测量/包装它?我知道其他Android视图必须做一些事情,因为如果设置为wrap_content,它们不会阻挡整个屏幕。提前谢谢!

2 个答案:

答案 0 :(得分:19)

This is the order这些常用的视图方法在:

中运行
1. Constructor    // choose your desired size 
2. onMeasure      // parent will determine if your desired size is acceptable
3. onSizeChanged
4. onLayout       
5. onDraw         // draw your view content at the size specified by the parent

选择所需的尺寸

如果您的视图可以是任何尺寸,它会选择多大的尺寸?这将是您的wrap_content尺寸,具体取决于您的自定义视图的内容。例子:

  • 如果您的自定义视图是图像,那么您所需的大小可能是位图的像素尺寸加上任何填充。 (在选择尺寸和绘制内容时,您有责任将填充计算到计算中。)
  • 如果自定义视图是模拟时钟,那么所需的大小可能是一些看起来很好的默认大小。 (您始终可以获得设备的dp to px size。)

如果您想要的大小使用繁重的计算,那么在构造函数中执行此操作。否则,您只需在onMeasure中分配即可。 (onMeasureonLayoutonDraw可能会被多次调用,这就是为什么在这里做繁重的工作并不好。)

谈判最终规模

onMeasure是孩子告诉父母有多大的地方,父母决定这是否可以接受。这种方法经常被调用几次,每次都传递不同的大小要求,看看是否可以达到一些妥协。但最终,孩子需要尊重父母的身材要求。

当我需要复习如何设置我的onMeasure时,我总是回到this answer

@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;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    //MUST CALL THIS
    setMeasuredDimension(width, height);
}

在上面的示例中,所需的宽度和高度仅设置为某些默认值。您可以事先计算它们,并使用类成员变量在此处设置它们。

使用所选尺寸

onMeasure之后,您的视图大小已知。这个大小可能是您要求的,也可能不是您所要求的,但这是您现在必须使用的。使用该尺寸在onDraw中的视图上绘制内容。

备注

  • 如果您对视图进行了更改而影响外观但不影响尺寸,请致电invalidate()。这将导致再次调用onDraw(但不是所有其他先前的方法)。
  • 如果您对视图进行了更改会影响尺寸,请致电requestLayout()。这将从onMeasure开始重新测量和绘制的过程。通常是combined with a call to invalidate()
  • 如果由于某种原因你事先无法确定合适的所需尺寸,那么我想你可以像@nmw建议那样做,只要求零宽度,零高度。然后在加载完所有内容后请求布局(不只是invalidate())。这看起来有点浪费,因为你要求整个视图层次结构连续布局两次。

答案 1 :(得分:3)

如果在onMeasure调用之前无法测量位图,则可以返回大小为零,直到加载位图。加载后,使父ViewGroup无效以强制执行另一个度量(不记得View本身上的invalidate()是否会强制onMeasure)。