Android onMeasure 自定义视图说明
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12266899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
onMeasure custom view explanation
提问by sennin
I tried to do custom component. I extended View
class and do some drawing in onDraw
overrided method. Why I need to override onMeasure
? If I didn't, everything seen to be right. May someone explain it? How should I write my onMeasure
method? 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. Where is a difference?
我试图做自定义组件。我扩展了View
类并在onDraw
覆盖的方法中进行了一些绘图。为什么我需要覆盖onMeasure
?如果我没有,一切都被认为是正确的。有人可以解释一下吗?我应该如何编写我的onMeasure
方法?我看过几个教程,但每一个都与另一个略有不同。有时他们super.onMeasure
在最后调用,有时他们使用setMeasuredDimension
并没有调用它。区别在哪里?
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. 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. When exactly I need to do that?
毕竟我想使用几个完全相同的组件。我将这些组件添加到我的XML
文件中,但我不知道它们应该有多大。我想稍后在自定义组件类中设置它的位置和大小(为什么我需要onMeasure
在onDraw
绘制它时设置大小,如果它也可以工作)。我什么时候需要这样做?
回答by Devunwired
onMeasure()
is your opportunity to tell Android how big you want your custom view to be dependent the layout constraints provided by the parent; 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). These constraints are packaged up into the MeasureSpec
values that are passed into the method. Here is a rough correlation of the mode values:
onMeasure()
是您告诉 Android 您希望自定义视图依赖于父级提供的布局约束有多大的机会;这也是您的自定义视图有机会了解这些布局约束是什么(如果您想在某种match_parent
情况下表现出与某种情况不同的行为wrap_content
)。这些约束被打包成MeasureSpec
传递给方法的值。以下是模式值的粗略相关性:
- EXACTLYmeans the
layout_width
orlayout_height
value was set to a specific value. You should probably make your view this size. This can also get triggered whenmatch_parent
is used, to set the size exactly to the parent view (this is layout dependent in the framework). - AT_MOSTtypically means the
layout_width
orlayout_height
value was set tomatch_parent
orwrap_content
where a maximum size is needed (this is layout dependent in the framework), and the size of the parent dimension is the value. You should not be any larger than this size. - UNSPECIFIEDtypically means the
layout_width
orlayout_height
value was set towrap_content
with no restrictions. 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.
- EXACTLY表示
layout_width
或layout_height
值已设置为特定值。您可能应该将视图设置为这种大小。这也可以在match_parent
使用时触发,以将大小完全设置为父视图(这取决于框架中的布局)。 - AT_MOST通常意味着
layout_width
或layout_height
值被设置为match_parent
或wrap_content
需要最大大小(这取决于框架中的布局),并且父维度的大小是该值。你不应该比这个尺寸大。 - UNSPECIFIED通常意味着
layout_width
或layout_height
值设置为wrap_content
没有限制。你可以是任何你想要的尺寸。一些布局还使用此回调来确定您想要的尺寸,然后再确定在第二个测量请求中实际再次传递给您的规格。
The contract that exists with onMeasure()
is that setMeasuredDimension()
MUSTbe called at the end with the size you would like the view to be. 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.
存在的契约onMeasure()
是setMeasuredDimension()
必须在最后以您希望视图的大小调用。此方法由所有框架实现调用,包括在 中找到的默认实现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.
希望有帮助。
回答by Florian
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:
实际上,您的答案并不完整,因为这些值还取决于包装容器。在相对或线性布局的情况下,这些值的行为如下:
- EXACTLYmatch_parent is EXACTLY + size of the parent
- AT_MOSTwrap_content results in an AT_MOST MeasureSpec
- UNSPECIFIEDnever triggered
- EXACTLYmatch_parent 是 EXACTLY + 父级的大小
- AT_MOSTwrap_content 导致 AT_MOST MeasureSpec
- UNSPECIFIED从未触发
In case of an horizontal scroll view, your code will work.
在水平滚动视图的情况下,您的代码将起作用。
回答by David Refaeli
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 piecethat claims "the default onMeasure() will always set a size of 100x100" - is wrong.
这个官方文档声称“默认的 onMeasure() 将始终设置 100x100 的大小” - 是错误的。