Why would I want to fitsSystemWindows?

System windows are the parts of the screen where the system is drawing either non-interactive (in the case of the status bar) or interactive (in the case of the navigation bar) content.

Most of the time, your app won’t need to draw under the status bar or the navigation bar, but if you do: you need to make sure interactive elements (like buttons) aren’t hidden underneath them. That’s what the default behavior of the android:fitsSystemWindows="true" attribute gives you: it sets the padding of the View to ensure the contents don’t overlay the system windows.

A few things to keep in mind:

  • fitsSystemWindows is applied depth first — ordering matters: it’s the first View that consumes the insets that makes a difference
  • Insets are always relative to the full window — insets may be applied even before layout happens, so don’t assume the default behavior knows anything about the position of a View when applying its padding
  • Any other padding you’ve set is overwritten — you’ll note that paddingLeft/paddingTop/etc is ineffective if you are using android:fitsSystemWindows="true" on the same View

And, in many cases, such as a full screen video playback, that’s enough. You’d have your full bleed view with no attribute and another full screen ViewGroup with android:fitsSystemWindows="true" for your controls that you want inset.

Or maybe you want your RecyclerView to scroll underneath a transparent navigation bar — by using android:fitsSystemWindows="true" in conjunction with android:clipToPadding="false", your scrolling content will be behind the controls but, when scrolled to the bottom, the last item will still be padded to be above the navigation bar (rather than hidden underneath!).

Customizing fitsSystemWindows

But this default behavior is just that: a default. On KitKat and below, your custom View could override fitSystemWindows() and provide any functionality you wanted — just return true if you’ve consumed the insets or false if you’d like to give other Views a chance.

However, on Lollipop and higher devices, we provide some new APIs to make customizing this behavior much easier and consistent with other behaviors for Views. You’ll instead override onApplyWindowInsets(), which allows the View to consume as much or as little of the insets as you need and be able to call dispatchApplyWindowInsets() on child views as needed.

Even better, you don’t even need to subclass your Views if you only need custom behavior on Lollipop and higher you can use ViewCompat.setOnApplyWindowInsetsListener(), which will be given preference over the View’s onApplyWindowInsets(). ViewCompat also provides helper methods for calling onApplyWindowInsets() and dispatchApplyWindowInsets() without version checking.

Examples of customizing fitsSystemWindows

While the basic layouts (FrameLayout, LinearLayout, etc) use the default behavior, there are a number of layouts that already customize how they react to fitsSystemWindows to fit specific use cases.

One example is the navigation drawer which needs to span the whole screen and appear under a transparent status bar.

Here, DrawerLayout uses fitsSystemWindows as a sign that it needs to inset its children (such as the main content view — just like the default behavior), but still draw the status bar background (which defaults to your theme’s colorPrimaryDark) in that space as per the material design specs.

You’ll note DrawerLayout calls dispatchApplyWindowInsets() for each child on Lollipop and higher to allow child views to also receive fitsSystemWindows, a departure from the default (where normally it would simply consume the insets and children would never receive fitsSystemWindows).

CoordinatorLayout also takes advantage of overriding how it handles window insets, allowing the Behavior set on child Views to intercept and change how Views react to window insets, before calling dispatchApplyWindowInsets() on each child themselves. It also uses the fitsSystemWindows flag to know if it needs to paint the status bar background.

Similarly, CollapsingToolbarLayout looks for fitsSystemWindows to determine when and where to draw the content scrim — a full-bleed scrim which overlays the status bar area when the CollapsingToolbarLayout is scrolled sufficiently off the screen.

If you’re interested in seeing some of the common cases that accompany the Design Library, check out the cheesesquare sample app.

Use the system, don’t fight it

One thing to keep in mind is that it isn’t called fitsStatusBar or fitsNavigationBar. What constitutes system windows, their dimensions, and location may certainly change with different platform releases — for a perfect example, look at the differences between Honeycomb and Ice Cream Sandwich.

Just rest assured that the insets you do get from fitsSystemWindows will be correct on all platform versions to ensure your content does not overlap with system provided UI components — make sure to avoid any assumptions on their availability or size if you customize the behavior.

#BuildBetterApps

Comment on the Google+ post and follow the Android Development Patterns Collection for more!