【问题标题】:ViewTreeObserver doesn't call onGlobalLayoutViewTreeObserver 不调用 onGlobalLayout
【发布时间】:2018-01-11 01:19:32
【问题描述】:

我已经有活动使用ViewTreeObserver 没有问题,但在这种情况下我没有收到onGlobalLayout 回调。

由于我在执行 http API 调用后获得了视图的宽度,因此似乎已经计算了宽度(由于 API 调用所花费的时间)。无论如何,为了确保我向ViewTreeObserver 添加了一个侦听器。但有时我没有收到回调(是的,有时)。

我可以在添加监听器之前检查宽度以避免等待回调,但我不知道为什么有时我没有收到回调。我检查了viewTreeObserver 是否始终存在。

ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();

assert viewTreeObserver.isAlive();

viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout()   // Not called sometimes, why?
    {
        view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        doSomething(view.getWidth());
    }
});

现在我将使用这个技巧:

int width = view.getWidth();

if (width > 0) {
    doSomething(view.getWidth());
} else {
    // use the ViewTreeObserver
}

编辑

以防万一,我做了这个辅助方法:

/**
 * Runs code on global layout event, useful when we need to do something after the layout is done
 * (like getting the view real measure). The runnable is only called once at most.
 *
 * A typical `shouldRun` function can be `v -> v.getWidth() > 0` since it checks that the view has some width,
 * so we can calculate things depending on that.
 *
 * @param shouldRun receives the `view` and decides if the runnable should run (it is checked when this method is called, and also on global layout).
 *
 * See: http://stackoverflow.com/questions/35443681/viewtreeobserver-doesnt-call-ongloballayout
 */
public static void runOnGlobalLayout(final View view, final Func1<View,Boolean> shouldRun, final Runnable runnable)
{
    if (shouldRun.call(view)) {
        runnable.run();
        return;
    }

    final ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();

    if (viewTreeObserver.isAlive()) {
        viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (shouldRun.call(view)) {
                    view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    runnable.run();
                }
            }
        });
    }
}

使用该方法,您可以执行以下操作:

runOnGlobalLayout(someLayout, v -> v.getWidth() > 0, () -> {
    int availableWidth = someLayout.getWidth();
    // draw things in layout, etc.
});

【问题讨论】:

    标签: android layout


    【解决方案1】:

    来自关于ViewTreeObserver.OnGlobalLayoutListener的官方文档:

    当全局布局状态或视图树中视图的可见性发生变化时要调用的回调的接口定义。

    如果您的 http 回调是在您的视图(以及他的子视图)的最后一次可见性更改之后发生的,那么您在 OnGlobalLayoutListener 中没有收到任何回调是正常的。

    您将收到回调的情况是您的 http 请求在您的所有视图创建之前完成,因此您将在您的onGlobalLayout 中收到一个回调,该回调指的是视图绘制的完成。

    【讨论】:

    • 谢谢!你知道如何告诉 Android:“嘿,你能告诉我这个视图什么时候计算出度量吗?”。
    • @FerranMaylinch 是:viewWithGlobalListener.requestLayout()
    • @FerranMaylinch 这将手动重新计算度量,因此它将进入 onGlobalLayout 回调
    • 在这种情况下,您在完成后请求另一个布局通道,不是吗?有没有办法避免这种情况?而且...如果我真的首先得到回调,请求布局会得到另一个回调,对吧?
    • @FerranMaylinch 是的,没错。如果您只想要一个回调,则必须实现自定义逻辑
    猜你喜欢
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 2016-11-06
    • 2021-10-29
    • 2014-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多