This is the order这些常用的视图方法都运行在:
1. Constructor // choose your desired size
2. onMeasure // parent will determine if your desired size is acceptable
3. onSizeChanged
4. onLayout
5. onDraw // draw your view content at the size specified by the parent
选择所需的尺寸
如果您的视图可以是它想要的任何尺寸,它会选择什么尺寸?这将是您的wrap_content 大小,取决于您的自定义视图的内容。例子:
- 如果您的自定义视图是图像,那么您所需的大小可能是位图的像素尺寸加上任何填充。 (在选择大小和绘制内容时,您有责任在计算中考虑填充。)
- 如果您的自定义视图是一个模拟时钟,那么所需的尺寸可能是它看起来不错的某个默认尺寸。 (您始终可以获取设备的
dp to px size。)
如果您想要的大小使用繁重的计算,请在您的构造函数中执行此操作。否则,您可以将其分配到onMeasure。 (onMeasure、onLayout 和 onDraw 可能会被多次调用,所以在这里做繁重的工作并不好。)
协商最终尺寸
onMeasure 是孩子告诉父母想要多大的地方,父母决定是否可以接受。这个方法经常被调用几次,每次都传入不同的大小要求,看看是否可以达成某种妥协。最后,孩子需要尊重父母的尺寸要求。
当我需要重新了解如何设置我的onMeasure 时,我总是会回到this answer:
@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 之后,您的视图大小是已知的。这个尺寸可能是也可能不是你要求的,但它是你现在必须使用的。使用该大小在 onDraw 中的视图上绘制内容。
注意事项
- 如果您对视图进行了影响外观但不影响大小的更改,请致电
invalidate()。这将导致再次调用 onDraw(但不是所有其他以前的方法)。
- 任何时候如果您对视图进行了会影响大小的更改,请致电
requestLayout()。这将从onMeasure 重新开始测量和绘图的过程。通常是combined with a call to invalidate()。
- 如果由于某种原因您确实无法事先确定合适的所需尺寸,那么我想您可以按照@nmw 的建议进行操作,只要求零宽度、零高度。然后在加载完所有内容后请求一个布局(不仅仅是
invalidate())。不过,这似乎有点浪费,因为您需要将整个视图层次结构连续布置两次。