onMeasure自定义视图说明

本文翻译自:onMeasure custom view explanation

I tried to do custom component. 我试着做自定义组件。 I extended View class and do some drawing in onDraw overrided method. 我扩展了View类并在onDraw覆盖方法中进行了一些绘制。 Why I need to override onMeasure ? 为什么我需要覆盖onMeasure If I didn't, everything seen to be right. 如果我没有,一切都被证明是正确的。 May someone explain it? 有人可以解释一下吗? How should I write my onMeasure method? 我该如何编写onMeasure方法? I've seen couple tutorials, but each one is a little bit different than the other. 我见过几个教程,但每个教程都有点不同。 Sometimes they call super.onMeasure at the end, sometimes they use setMeasuredDimension and didn't call it. 有时他们最后会调用super.onMeasure ,有时他们使用setMeasuredDimension并且没有调用它。 Where is a difference? 差异在哪里?

After all I want to use several exactly the same components. 毕竟我想要使用几个完全相同的组件。 I added those components to my XML file, but I don't know how big they should be. 我将这些组件添加到我的XML文件中,但我不知道它们应该有多大。 I want to set its position and size later (why I need to set size in onMeasure if in onDraw when I draw it, is working as well) in custom component class. 我想稍后设置它的位置和大小(为什么我需要在onMeasure设置大小,如果我在onDraw绘制它,也可以在自定义组件类中设置)。 When exactly I need to do that? 什么时候我需要这样做?


#1楼

参考:https://stackoom.com/question/pTBD/onMeasure自定义视图说明


#2楼

onMeasure() is your opportunity to tell Android how big you want your custom view to be dependent the layout constraints provided by the parent; onMeasure()是告诉Android您希望自定义视图依赖于父级提供的布局约束的大小的机会; it is also your custom view's opportunity to learn what those layout constraints are (in case you want to behave differently in a match_parent situation than a wrap_content situation). 您也可以通过自定义视图了解这些布局约束是什么(如果您希望在match_parent情况下的行为与wrap_content情况不同)。 These constraints are packaged up into the MeasureSpec values that are passed into the method. 这些约束被打包到传递给方法的MeasureSpec值中。 Here is a rough correlation of the mode values: 以下是模式值的粗略关联:

  • EXACTLY means the layout_width or layout_height value was set to a specific value. 确切意味着layout_widthlayout_height值设置为特定值。 You should probably make your view this size. 您可能应该将视图设为这么大。 This can also get triggered when match_parent is used, to set the size exactly to the parent view (this is layout dependent in the framework). 这也可以在使用match_parent时触发,以将大小精确地设置为父视图(这取决于框架中的布局)。
  • AT_MOST typically means the layout_width or layout_height value was set to match_parent or wrap_content where a maximum size is needed (this is layout dependent in the framework), and the size of the parent dimension is the value. AT_MOST通常表示layout_widthlayout_height值设置为match_parentwrap_content ,其中需要最大大小(这取决于框架中的布局),并且父维度的大小是值。 You should not be any larger than this size. 你不应该大于这个尺寸。
  • UNSPECIFIED typically means the layout_width or layout_height value was set to wrap_content with no restrictions. UNSPECIFIED通常表示layout_widthlayout_height值设定为wrap_content没有任何限制。 You can be whatever size you would like. 你可以任意大小。 Some layouts also use this callback to figure out your desired size before determine what specs to actually pass you again in a second measure request. 某些布局还会使用此回调来确定所需的大小,然后再确定在第二个度量请求中实际传递给您的规范。

The contract that exists with onMeasure() is that setMeasuredDimension() MUST be called at the end with the size you would like the view to be. onMeasure()存在的契约是setMeasuredDimension() 必须在末尾调用您希望视图的大小。 This method is called by all the framework implementations, including the default implementation found in View , which is why it is safe to call super instead if that fits your use case. 所有框架实现都会调用此方法,包括View的默认实现,这就是为什么在适合您的用例时调用super是安全的。

Granted, because the framework does apply a default implementation, it may not be necessary for you to override this method, but you may see clipping in cases where the view space is smaller than your content if you do not, and if you lay out your custom view with wrap_content in both directions, your view may not show up at all because the framework doesn't know how large it is! 当然,因为框架确实应用了默认实现,所以您可能没有必要覆盖此方法,但如果您不这样做,您可能会在视图空间小于您的内容的情况下看到裁剪,如果您布局了自定义视图在两个方向都有wrap_content ,您的视图可能根本不显示,因为框架不知道它有多大!

Generally, if you are overriding View and not another existing widget, it is probably a good idea to provide an implementation, even if it is as simple as something like this: 通常,如果您要覆盖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);
}

Hope that Helps. 希望有助于。


#3楼

actually, your answer is not complete as the values also depend on the wrapping container. 实际上,您的答案并不完整,因为值也取决于包装容器。 In case of relative or linear layouts, the values behave like this: 在相对或线性布局的情况下,值的行为如下:

  • EXACTLY match_parent is EXACTLY + size of the parent 完全匹配_parent是完全+父级的大小
  • AT_MOST wrap_content results in an AT_MOST MeasureSpec AT_MOST wrap_content导致AT_MOST MeasureSpec
  • UNSPECIFIED never triggered UNSPECIFIED从未触发

In case of an horizontal scroll view, your code will work. 如果是水平滚动视图,您的代码将起作用。


#4楼

If you don't need to change something onMeasure - there's absolutely no need for you to override it. 如果您不需要更改onMeasure上的内容 - 您绝对不需要覆盖它。

Devunwired code (the selected and most voted answer here) is almost identical to what the SDK implementation already does for you (and I checked - it had done that since 2009). Devunwired代码(这里选择和最多的投票答案)几乎与SDK实现已经为您做的(我检查过 - 自2009年以来已经完成)。

You can check the onMeasure method here : 你可以在这里查看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;
}

Overriding SDK code to be replaced with the exact same code makes no sense. 覆盖用完全相同的代码替换的SDK代码是没有意义的。

This official doc's piece that claims "the default onMeasure() will always set a size of 100x100" - is wrong. 这篇官方文档声称“默认onMeasure()将始终设置为100x100”的大小 - 是错误的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章