Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewTreeObserver doesn't call onGlobalLayout

Tags:

android

layout

I already have activities where I'm using the ViewTreeObserver without problems, but in this case I don't get the onGlobalLayout callback.

Since I get the width of a view after I perform an http API call, the width seems to be already calculated (due to the time that the API call takes). Anyway, to be sure I add a listener to the ViewTreeObserver. But sometimes I don't get the callback (yes, sometimes).

I can check the width before adding the listener to avoid having to wait for the callback, but I don't know why I'm not getting the callback sometimes. I have checked that the viewTreeObserver is always alive.

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());
    }
});

For now I will use this trick:

int width = view.getWidth();

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

EDIT:

Just in case it helps, I made this helper method:

/**
 * 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();
                }
            }
        });
    }
}

With that method you can do for example:

runOnGlobalLayout(someLayout, v -> v.getWidth() > 0, () -> {
    int availableWidth = someLayout.getWidth();
    // draw things in layout, etc.
});
like image 884
Ferran Maylinch Avatar asked Feb 16 '16 21:02

Ferran Maylinch


1 Answers

From the official documentation about ViewTreeObserver.OnGlobalLayoutListener:

Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.

If your http callback comes after the last visibility change of your view (with his subviews) it's normal that you aren't getting any callback in your OnGlobalLayoutListener.

The case in which you will get a callback is when your http request is finished before all your views are created, so you will get a callback in your onGlobalLayout that is referred to the finish of the drawing of the views.

like image 185
Giorgio Antonioli Avatar answered Nov 09 '22 18:11

Giorgio Antonioli