什么时候调用View.onMeasure()?

时间:2011-07-08 22:12:29

标签: java android android-activity view onmeasure

什么时候

View.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

叫什么?我有Activity需要在调用onMeasure后执行操作。

我的问题与the unanswered question posted here相同。 View文档声明onMeasure在调用requestLayout()时被调用,当它认为它不再适合其当前范围时,它本身就被一个视图调用。

然而,这并没有告诉我何时我的活动可以假设我的View已被测量。我已使用this code扩展ImageView以形成TouchImageView。 It was suggested here我应该使用onMeasure方法来扩展我的形象。我希望在测量TextView之后更新ImageView的值,以便显示缩放图像的百分比。

2 个答案:

答案 0 :(得分:15)

您可以在onSizeChanged中获取自定义视图的尺寸。

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    // use the provided width and height for whatever you need
}

解释

创建视图时,这是调用以下方法的顺序:

  1. 构造
    • CustomView(Context context)(如果以编程方式创建)
    • CustomView(Context context, AttributeSet attrs)(如果是从xml创建的)
  2. onFinishInflate(假设您使用了xml构造函数)
  3. onAttachedToWindow
  4. onMeasure
  5. onSizeChanged
  6. onLayout
  7. onDraw
  8. 最早可以获得视图的测量值在onMeasure。在此之前,宽度和高度为0。但是,您在onMeasure中应该做的唯一事情是确定视图的大小。当视图告诉父母它想要多大但父母正在确定实际的最终大小时,会多次调用此方法。 (有关onMeasure的使用方式,请参阅this answer。)

    如果您想实际使用测量的尺寸,最早的地方是onSizeChanged。每当创建视图时都会调用它,因为大小从0变为任何大小。

    您也可以使用onLayout,但据我了解,onLayout用于自定义自定义视图的任何子项的布局方式。它也可能比onSizeChanged更频繁地被调用,例如,如果在实际没有更改大小时调用requestLayout()

    您还可以使用onDrawgetMeasuredWidth()访问getMeasuredHeight()中的尺寸。但是,如果您使用它们进行任何繁重的计算,最好事先做好。一般来说,尽量保持尽可能多的onDraw,因为它可能被多次调用。 (只要调用invalidate()就会调用它。)

    亲自看看

    如果您不相信我,您可以在下面的自定义视图中查看事件的顺序。这是输出:

    XML constructor called, measured size: (0, 0)
    onFinishInflate called, measured size: (0, 0)
    onAttachedToWindow called, measured size: (0, 0)
    onMeasure called, measured size: (350, 1859)
    onMeasure called, measured size: (350, 350)
    onMeasure called, measured size: (350, 2112)
    onMeasure called, measured size: (350, 350)
    onSizeChanged called, measured size: (350, 350)
    onLayout called, measured size: (350, 350)
    onDraw called, measured size: (350, 350)
    

    activity_main.xml中

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.example.viewlifecycle.CustomView
            android:id="@+id/customView"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@color/colorAccent"/>
    
    </RelativeLayout>
    

    CustomView.java

    public class CustomView extends View {
    
        private void printLogInfo(String methodName) {
            Log.i("TAG", methodName + " called, measured size: (" + getMeasuredWidth() + ", " + getMeasuredHeight() + ")");
        }
    
        // constructors
    
        public CustomView(Context context) {
            super(context);
            printLogInfo("Programmatic constructor");
        }
    
        public CustomView(Context context, AttributeSet attrs) {
            super(context, attrs);
            printLogInfo("XML constructor");
        }
    
        // lifecycle methods
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            printLogInfo("onFinishInflate");
        }
    
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            printLogInfo("onAttachedToWindow");
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            printLogInfo("onMeasure");
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            printLogInfo("onSizeChanged");
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            printLogInfo("onLayout");
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            printLogInfo("onDraw");
        }
    }
    

    进一步阅读

答案 1 :(得分:14)

当父View需要计算布局时,会调用onMeasure。通常,onMeasure可能会被调用多次,具体取决于不同的孩子及其布局参数。

调用onMeasure时执行某些操作的最佳方法是(我认为)创建自己的控件,扩展ImageView并覆盖onMeasure(只需调用super.onMeasure并执行其他任何操作喜欢这样做。

如果你这样做,请记住,在测量时可能会使用不同的参数多次调用,因此测量的内容可能不是实际显示的最终宽度和高度。