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