我试着做自定义组件。我扩展了View
类并在onDraw
覆盖方法中做了一些绘图。为什么我需要覆盖onMeasure
?如果我没有,一切都被证明是正确的。有人可以解释一下吗?我该怎么写我的onMeasure
方法?我见过几个教程,但每个教程都有点不同。有时他们最后打电话给super.onMeasure
,有时他们使用setMeasuredDimension
并没有打电话给它。差异在哪里?
毕竟我想要使用几个完全相同的组件。我将这些组件添加到我的XML
文件中,但我不知道它们应该有多大。我想稍后设置它的位置和大小(为什么我需要在onMeasure
中设置大小,如果我在onDraw
中绘制它,也可以在自定义组件类中设置)。什么时候我需要这样做?
onMeasure()
是您告诉Android您希望自定义视图依赖于父级提供的布局约束的大小的机会;这也是你的自定义视图有机会了解这些布局约束(如果你想在match_parent
情况下表现不同,而不是wrap_content
情况)。这些约束被打包到传递给方法的MeasureSpec
值中。以下是模式值的粗略关联:
layout_width
或layout_height
值设置为特定值。您可能应该将视图设为这么大。当使用match_parent
时,也可以触发此操作,将大小精确地设置为父视图(这取决于框架中的布局)。layout_width
或layout_height
值设置为match_parent
或wrap_content
,其中需要最大大小(这是布局依赖于框架),并且父维度的大小是值。你不应该大于这个尺寸。layout_width
或layout_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);
}
希望有助于。
实际上,您的答案并不完整,因为值也取决于包装容器。在相对或线性布局的情况下,值的行为如下:
如果是水平滚动视图,您的代码将起作用。
如果您不需要更改onMeasure上的内容 - 您绝对不需要覆盖它。
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()将始终设置为100x100”的official doc's piece是错误的。