onMeasure自定义视图说明

sen*_*nin 305 android view

我试着做自定义组件.我扩展了View课程并用onDraw覆盖方法做了一些绘图.为什么我需要覆盖onMeasure?如果我没有,一切都被证明是正确的.有人可以解释一下吗?我该怎么写我的onMeasure方法?我见过几个教程,但每个教程都有点不同.有时他们会super.onMeasure在最后打电话,有时他们会使用setMeasuredDimension而不是打电话.差异在哪里?

毕竟我想要使用几个完全相同的组件.我将这些组件添加到我的XML文件中,但我不知道它们应该有多大.我希望稍后设置它的位置和大小(为什么我需要在自定义组件类时设置大小,onMeasure如果在onDraw我绘制它时也是如此).什么时候我需要这样做?

Dev*_*red 712

onMeasure()您有机会告诉Android您希望您的自定义视图有多大依赖于父级提供的布局约束; 您也可以通过自定义视图了解这些布局约束是什么(如果您希望在某种match_parent情况下表现出不同的wrap_content情况).这些约束被打包到MeasureSpec传递给方法的值中.以下是模式值的粗略关联:

  • 确切意味着layout_widthlayout_height值设置为特定值.您可能应该将视图设为这么大.这也可以在match_parent使用时触发,将大小精确地设置为父视图(这是在框架中依赖于布局).
  • AT_MOST通常表示layout_width或者layout_height值设置为match_parentwrap_content需要最大大小(这是在框架中依赖于布局),并且父维度的大小是值.你不应该大于这个尺寸.
  • UNSPECIFIED通常表示layout_widthlayout_height值设定为wrap_content没有任何限制.你可以任意大小.某些布局还会使用此回调来确定所需的大小,然后再确定在第二个度量请求中实际传递给您的规范.

与存在的合同onMeasure()setMeasuredDimension() 必须在与大小结束时调用你想的观点是.所有框架实现都会调用此方法,包括找到的默认实现View,这就是为什么super如果适合您的用例则可以安全地调用它.

当然,因为框架确实应用了默认实现,所以您可能没有必要覆盖此方法,但如果您不这样做,您可能会在视图空间小于您的内容的情况下看到裁剪,如果您布局wrap_content在两个方向上的自定义视图,您的视图可能根本不显示,因为框架不知道它有多大!

通常,如果你是覆盖View而不是另一个现有的小部件,那么提供一个实现可能是一个好主意,即使它像这样简单:

@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);
}
Run Code Online (Sandbox Code Playgroud)

希望有助于.

  • 请注意,如果您覆盖任何ViewGroup子类的onMeasure,则此代码将不会执行此操作.您的子视图不会显示,并且都将具有0x0的大小.如果需要覆盖自定义ViewGroup的onMeasure,请更改widthMode,widthSize,heightMode和heightSize,使用MeasureSpec.makeMeasureSpec将它们编译回measureSpecs并将结果整数传递给super.onMeasure. (44认同)
  • 复杂的c**p使Android成为一个痛苦的布局系统.他们可能只有getParent().get***()... (4认同)
  • 是的,这就是“ViewGroup”的“measureChildren()”方法在测量/布局过程中所做的事情。 (2认同)
  • View类中有一些辅助方法,分别称为`resolveSizeAndState`和`resolveSize`,它们应该执行if子句的作用-我发现它们很有用,尤其是当您必须经常编写这些IF时。 (2认同)

Flo*_*ian 5

实际上,您的答案并不完整,因为这些值还取决于包装容器。在相对或线性布局的情况下,这些值的行为如下:

  • EXACTLY match_parent 是 EXACTLY + 父级的大小
  • AT_MOST wrap_content 导致 AT_MOST MeasureSpec
  • UNSPECIFIED从未触发

在水平滚动视图的情况下,您的代码将起作用。

  • 如果您觉得这里的某些答案不完整,请补充而不是给出部分答案。 (57认同)

Dav*_*eli 5

如果您不需要更改 onMeasure 的某些内容 - 则绝对不需要覆盖它。

Devunwired 代码(这里选择的和投票最多的答案)几乎与 SDK 实现已经为您所做的事情相同(我检查过 - 自 2009 年以来它已经这样做了)。

您可以在此处检查 onMeasure 方法:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

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;
}
Run Code Online (Sandbox Code Playgroud)

用完全相同的代码替换 SDK 代码是没有意义的。

这个官方文档声称“默认的 onMeasure() 将始终设置 100x100 的大小” - 是错误的。