onMeasure(): wrap_content,我怎么知道要包装的尺寸?

Posted

技术标签:

【中文标题】onMeasure(): wrap_content,我怎么知道要包装的尺寸?【英文标题】:onMeasure(): wrap_content, how do I know the size to wrap? 【发布时间】:2012-10-27 18:03:02 【问题描述】:

我创建了一个自定义视图,覆盖了onDraw(),它在画布上绘制位图。当我在布局文件中指定我希望它 wrap_content 时,它仍然会填满整个屏幕。 onMeasure() 是这样说的:

measure 的基类实现默认为背景大小,除非MeasureSpec 允许更大的大小。子类应覆盖 onMeasure(int, int) 以更好地测量其内容。

好吧,我知道我需要覆盖 onMeasure() 并使用 MeasureSpec。根据this answer

未指定意味着 layout_width 或 layout_height 值被设置为 wrap_content。你可以是任何你想要的尺寸。

现在我解决了我的问题,我如何在onMeasure() 测量我尚未创建的位图并测量/包装它?我知道其他 android 视图必须做一些事情,因为如果设置为 wrap_content,它们不会挡住整个屏幕。提前致谢!

【问题讨论】:

【参考方案1】:

如果您无法在 onMeasure 调用之前测量位图,那么您可以在加载位图之前返回零大小。加载后,使父 ViewGroup 无效以强制执行另一个度量(不记得视图本身上的 invalidate() 是否会强制执行 onMeasure)。

【讨论】:

经过一番挖掘,我发现你说的是对的。创建视图后,我给它位图并调用 requestLayout();它调用了我的 onMeasure 并使视图大小合适。谢谢! @nmw 我遇到了同样的问题,但没有想到阅读上面的答案。您能否在评论中添加更多解释或对上面的答案进行编辑。这对我有很大帮助。【参考方案2】:

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。 (onMeasureonLayoutonDraw 可能会被多次调用,所以在这里做繁重的工作并不好。)

协商最终尺寸

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())。不过,这似乎有点浪费,因为您需要将整个视图层次结构连续布置两次。

【讨论】:

以上是关于onMeasure(): wrap_content,我怎么知道要包装的尺寸?的主要内容,如果未能解决你的问题,请参考以下文章

Android查缺补漏(View篇)--自定义 View 中 wrap_content 无效的解决方案

具有不同项目高度和 WRAP_CONTENT 的 ViewPager2

Android面试自定义View

Android自定义View(初体验自定义TextView)

Android-ViewPager源码解析与性能优化

自定义视图测量