android onMeasure自定义视图说明

ybzsozfc  于 2022-12-25  发布在  Android
关注(0)|答案(3)|浏览(165)

我试着做自定义组件。我扩展了View类,并在onDraw重写的方法中做了一些绘图。为什么我需要重写onMeasure?如果我没有,一切看起来都是正确的。有人能解释一下吗?我应该如何写我的onMeasure方法?我看过一些教程,但是每个都有一点不同,有时候他们在最后调用super.onMeasure,有时候他们使用setMeasuredDimension,但是没有调用,区别在哪里?
毕竟我想使用几个完全相同的组件。我把这些组件添加到我的XML文件中,但我不知道它们应该有多大。我想稍后在自定义组件类中设置它的位置和大小(为什么我需要在onMeasure中设置大小,如果我绘制它时在onDraw中也能工作的话)。我到底什么时候需要这样做?

3npbholx

3npbholx1#

onMeasure()是您告诉Android您希望您的自定义视图依赖于父视图提供的布局约束的机会;这也是您的自定义视图了解这些布局约束的机会(如果您希望在match_parent情况下的行为与在wrap_content情况下的行为有所不同)。这些约束被打包到传递给方法的MeasureSpec值中。

*EXACTLY表示layout_widthlayout_height值已设置为特定值。您可能应该将视图设置为该大小。使用match_parent时也会触发此操作,以将大小精确设置为父视图(这在框架中取决于布局)。
*AT_MOST通常表示layout_widthlayout_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);
}
sdnqo3pr

sdnqo3pr2#

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

*完全匹配match_parent是完全匹配+父项的大小
*AT_MOST包裹内容产生AT_MOST测量规范
*未指定从未触发

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

ghhaqwfi

ghhaqwfi3#

如果您不需要更改Measure上的某些内容-您绝对没有必要覆盖它。
Devunwired代码(这里选择的和投票最多的答案)几乎与SDK实现已经为您做的事情相同(我检查过了-它从2009年就开始这样做了)。
您可以检查onMeasure方法here

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

用完全相同的代码覆盖要替换的SDK代码是没有意义的。
这篇官方文档声称“默认的onMeasure()将始终设置100 x100的大小”是错误的。

相关问题