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.
});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With