何时调用 View.onMeasure()?

何时是

View.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

叫?我有一个活动,需要在调用 onMeasure 后执行操作。

我的问题与这里发布的未回答的问题相同。View 文档指出,当 requestLayout() 被调用时,onMeasure 被调用,当它认为它不能再适合其当前边界时,它显然是由自身视图调用的。

但是,这并不能告诉我我的活动何时可以假设我的视图已被测量。我用这段代码扩展了ImageView,形成了TouchImageView。这里建议我应该使用 onMeasure 方法来缩放我的图像。我希望在测量图像视图后更新文本视图的值,以显示图像缩放的百分比。


答案 1

您可以在 中获取自定义视图的测量值。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

最早可以获得视图的测量值是在 中。在此之前,宽度和高度是 .但是,您唯一应该做的就是确定视图的大小。此方法被调用多次,而视图告诉父级它想要多大,但父级正在确定实际的最终大小。(请参阅此答案,了解如何使用。onMeasure0onMeasureonMeasure

如果要实际将测量的大小用于任何操作,最早可以这样做的地方是 。每当创建视图时都会调用它,因为大小从更改为任何大小。onSizeChanged0

您也可以使用 ,尽管据我所知,它用于自定义自定义视图的任何子视图的布局方式。它也可能比 更频繁地被调用,例如,如果您在大小实际上没有变化时调用。onLayoutonLayoutonSizeChangedrequestLayout()

您还可以使用 和 访问 中的大小。但是,如果您使用它们进行任何繁重的计算,则最好事先执行此操作。一般来说,尽量避免使用,因为它可能会被多次调用。(每当被调用时,它就会被调用。onDrawgetMeasuredWidth()getMeasuredHeight()onDrawinvalidate()

亲眼看看

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

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>

自定义视图.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");
    }
}

进一步阅读


答案 2

当父视图需要计算布局时,将调用 onMeasure。通常,onMeasure 可以调用多次,具体取决于存在的不同子级及其布局参数。

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

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


推荐