WindowInsets??
Have you ever wondered what are WindowInsets used for? Why would you ever need them? You have seen people applying android:fitsSystemWindows=”true” to their layouts but you actually see no difference sometimes? What an API is this? We’ll try to demystify them in this article.
WindowInsets
are insets (or sizes) of system views (e.g. status bar, navigation bar), that are applied to the window of your application.
The best way to understand something — is to see it in example. Let’s imagine you have requirement to achieve this UI:
How would I do that?
Well, there is aToolbar
and an ImageView
in this layout. I’d wrap them in FrameLayout
and that’s it, right? Not yet. What I will get there is that theToolbar
would be stretched by status bar, i.e. Toolbar
will not have necessary top padding in order to be laid out below status bar.
What if I apply android:fitsSystemWindows=”true” to the FrameLayout
?
First, let’s understand what this flag is going to do. When a layout has this flag, it means that this layout is asking the system to be applied window insets. In our case that would mean, that FrameLayout
desires to be applied a padding that is equal to status bar’s height.
Well, that’s nice. Is seems like a good idea, that will solve our problem. Actually it won’t, because now the ImageView
will also receive this padding, and will be laid out below the status bar.
Luckily, there’s a nice API, that solves our problem — ViewCompat.setOnApplyWindowInsetListener
. With this API you can apply window insets to a particular view.
ViewCompat.setOnApplyWindowInsetsListener(toolbar, (v, insets) -> {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
params.topMargin = insets.getSystemWindowInsetTop();
return insets.consumeSystemWindowInsets();
});
But insets.getSystemWindowInsetTop()
would return 0 in our case, because, remember, we have wrapped our view in FrameLayout
.
Standard layouts like FrameLayout
, LinearLayout
or RelativeLayout
will not pass window insets to their children, whereas “materialish” layouts will do (e.g. DrawerLayout
, CoordinatorLayout
). So we may change our layout to “materialish” one, or we can subclass a standard layout and pass the insets to the children of layout ourselves. We just have to override onApplyWindowInsets
method.
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int childCount = getChildCount();
for (int index = 0; index < childCount; ++index)
getChildAt(index).dispatchApplyWindowInsets(insets);
// let children know about WindowInsets
return insets;
}
That’s it. Now we have the UI we wanted to achieve.
You may also read an article by Ian Lake and a presentation from Chris Banes concerning window insets.
Thanks for reading. Happy droiding! 🤖