首页 文章

onMeasure自定义视图说明

提问于
浏览
286

我试着做自定义组件 . 我扩展了 View 类并在 onDraw 覆盖方法中进行了一些绘制 . 为什么我需要覆盖 onMeasure ?如果我没有,一切都被证明是正确的 . 有人可以解释一下吗?我该如何编写 onMeasure 方法?我见过几个教程,但每个教程都有点不同 . 有时他们最后会调用 super.onMeasure ,有时候他们会使用 setMeasuredDimension 并且没有调用它 . 区别在哪里?

毕竟我想使用几个完全相同的组件 . 我将这些组件添加到我的 XML 文件中,但我不知道它们应该有多大 . 我想稍后设置它的位置和大小(为什么我需要在自定义组件类中设置 onMeasure 中的大小,如果在 onDraw 中绘制它,也正常工作) . 什么时候我需要这样做?

2 回答

  • 687

    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() 一起存在的 Contract 是 setMeasuredDimension() MUST 最后以您希望视图的大小调用 . 所有框架实现都会调用此方法,包括 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);
    }
    

    希望有帮助 .

  • 4

    实际上,您的答案并不完整,因为值也取决于包装容器 . 在相对或线性布局的情况下,值的行为如下:

    • EXACTLY match_parent是父级的完全大小

    • AT_MOST wrap_content导致AT_MOST MeasureSpec

    • UNSPECIFIED 从未触发过

    如果是水平滚动视图,您的代码将起作用 .

相关问题