【问题标题】:When is View.onMeasure() called?何时调用 View.onMeasure()?
【发布时间】:2011-10-01 15:30:43
【问题描述】:

什么时候

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 方法来缩放我的图像。我希望在测量ImageView 之后更新TextView 的值,以显示图像缩放的百分比。

【问题讨论】:

  • 因为我只有 10 分,所以我无法添加两个以上的链接,即使它们指向像 stackoverflow.com 和 developer.android.com 这样的域。感谢您修复我的链接,但您不必要地删除了我的报价。由于我的限制,我无法在不删除链接的情况下修复它。
  • 抱歉,我没有看到我删除了哪些引用。如果你告诉我,我可以把它放回去……
  • “当一个视图认为它不再适合其当前范围时,它会调用它自己”是从View 文档中逐字提取的。里面有错别字。

标签: java android android-activity view 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

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

如果您想实际使用测量的大小,最早可以在onSizeChanged 中执行此操作。每当创建视图时都会调用它,因为大小从 0 变为任何大小。

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

您还可以使用getMeasuredWidth()getMeasuredHeight() 访问onDraw 中的大小。但是,如果您使用它们进行任何繁重的计算,最好事先进行。一般来说,尽量远离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");
    }
}

进一步阅读

【讨论】:

  • Constructor 是指onCreate
  • @dankal444,不,自定义视图没有onCreate 方法。不过,构造函数 用于创建新视图对象的方法。在我上面的 CustomView.java 示例中,有两个构造方法:CustomView(Context context)CustomView(Context context, AttributeSet attrs)
【解决方案2】:

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

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

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

【讨论】:

  • 感谢您回答我的问题,但我相信您错过了我的意思。正如我在问题中所说,我确实扩展了 ImageView 并覆盖了 onMeasure 方法。但是,有一个属于我的活动的对象(一个 TextView)需要在调用 onMeasure 后更新。
  • 只需在你的类中添加一个扩展 ImageView 的函数 setAssociatedTextView(TextView tv) 并在你的 onMeasure 函数中使用它,不确定问题是什么......?但同样,您无法确定视图何时被完全测量(因为 onMeasure 很可能会被调用多次)
  • 这解决了我的问题,但感觉很脏。我确定我必须违反某种与依赖有关的设计原则最佳实践。我不得不将 Activity 中的代码复制到我的 TouchImageView(ImageView 扩展)中,现在 Activity 依赖于视图,其中一个视图依赖于另一个。不过,它可以工作,所以感谢您的解决方案。
猜你喜欢
  • 2011-12-07
  • 1970-01-01
  • 2016-02-11
  • 2019-02-05
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 2012-06-14
相关资源
最近更新 更多