Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Becoming a master window fitter (Droidcon NYC 2017)

Chris Banes
September 27, 2017

Becoming a master window fitter (Droidcon NYC 2017)

Window insets have long been a source of confusion to developers, and that’s because they are indeed very confusing! The system dispatches insets for many reasons, such as drawing behind navigation bars, full-screen immersive modes or handling round displays.

This session will deep-dive on the situations where you need to consider Window Insets, and how you can handle them without resorting to copying random code from StackOverflow. You will learn the answers to questions such as:

‘How do I handle these in my custom view?’, ‘How do I draw behind the status bar?’ and ‘What was the developer of AppBarLayout thinking?!’

Video: https://chris.banes.me/talks/2017/becoming-a-master-window-fitter-nyc/

Chris Banes

September 27, 2017
Tweet

More Decks by Chris Banes

Other Decks in Technology

Transcript

  1. S

  2. Inset How the system tells you where the system ui

    currently is, or may later be displayed
  3. Rule of thumb You probably want to avoid the default

    behavior of android:fitSystemWindows
  4. 12:00 <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end" /> <LinearLayout

    layout_height="match_parent" layout_width="match_parent">
 <!— toolbar and other content —> </LinearLayout> </DrawerLayout> >
  5. 12:00 > fitSystemWindows="true" <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end"

    /> <LinearLayout layout_height="match_parent" layout_width="match_parent">
 <!— toolbar and other content —> </LinearLayout> </DrawerLayout>
  6. DrawerLayout Sets the WTFs for you on API 21+ 12:00

    Use fitSystemWindows as a signal from children
  7. fitSystemWindows="true"> /> fitSystemWindows="true" <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end"

    <LinearLayout layout_height="match_parent" layout_width="match_parent"> <!— toolbar and other content —> </LinearLayout> </DrawerLayout> 12:00
  8. fitSystemWindows="true"> /> fitSystemWindows="true" <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end"

    <LinearLayout layout_height="match_parent" layout_width="match_parent"> <!— toolbar and other content —> </LinearLayout> </DrawerLayout> 12:00
  9. fitSystemWindows="true"> /> fitSystemWindows="true" <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end"

    <LinearLayout layout_height="match_parent" layout_width="match_parent" <!— toolbar and other content —> </LinearLayout> </DrawerLayout> > 12:00
  10. fitSystemWindows="true"> /> fitSystemWindows="true" <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end"

    <LinearLayout layout_height="match_parent" layout_width="match_parent" <!— toolbar and other content —> </LinearLayout> </DrawerLayout> fitSystemWindows="true"> 12:00
  11. fitSystemWindows="true"> /> fitSystemWindows="true" <DrawerLayout layout_height="match_parent" layout_width="match_parent" <NavigationView layout_height="match_parent" layout_width="match_parent"
 layout_gravity="end"

    <LinearLayout layout_height="match_parent" layout_width="match_parent" <!— toolbar and other content —> </LinearLayout> </DrawerLayout> 12:00 fitSystemWindows="true">
  12. 12:00 Sets the WTFs for you on API 21+ Use

    fitSystemWindows as a signal from children CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout
  13. <resources> <!-- The width that is used when creating thumbnails

    of applications. --> <dimen name="thumbnail_width">192dp</dimen> <!-- The height that is used when creating thumbnails of applications. --> <dimen name="thumbnail_height">192dp</dimen> <!-- The amount to scale a fullscreen screenshot thumbnail. --> <item name="thumbnail_fullscreen_scale" type="fraction">60%</item> <!-- The width used to calculate scale for full screen thumbnail on TV --> <integer name="thumbnail_width_tv">240</integer> <!-- The standard size (both width and height) of an application icon that will be displayed in the app launcher and elsewhere. --> <dimen name="app_icon_size">48dip</dimen> <dimen name="toast_y_offset">64dip</dimen> <!-- Height of the status bar --> <dimen name="status_bar_height">24dp</dimen> <!-- Height of the bottom navigation / system bar. --> <dimen name="navigation_bar_height">48dp</dimen> <!-- Height of the bottom navigation bar in portrait; often the same as
 @dimen/navigation_bar_height --> <dimen name="navigation_bar_height_landscape">48dp</dimen> <!-- Width of the navigation bar when it is placed vertically on the screen --> <dimen name="navigation_bar_width">48dp</dimen> <!-- Height of the bottom navigation / system bar in car mode. --> frameworks/base/core/res/values/dimens.xml
  14. frameworks/base/core/res/values/dimens.xml <resources> <!-- The width that is used when creating

    thumbnails of applications. --> <dimen name="thumbnail_width">192dp</dimen> <!-- The height that is used when creating thumbnails of applications. --> <dimen name="thumbnail_height">192dp</dimen> <!-- The amount to scale a fullscreen screenshot thumbnail. --> <item name="thumbnail_fullscreen_scale" type="fraction">60%</item> <!-- The width used to calculate scale for full screen thumbnail on TV --> <integer name="thumbnail_width_tv">240</integer> <!-- The standard size (both width and height) of an application icon that will be displayed in the app launcher and elsewhere. --> <dimen name="app_icon_size">48dip</dimen> <dimen name="toast_y_offset">64dip</dimen> <!-- Height of the status bar --> <dimen name="status_bar_height">24dp</dimen> <!-- Height of the bottom navigation / system bar. --> <dimen name="navigation_bar_height">48dp</dimen> <!-- Height of the bottom navigation bar in portrait; often the same as
 @dimen/navigation_bar_height --> <dimen name="navigation_bar_height_landscape">48dp</dimen> <!-- Width of the navigation bar when it is placed vertically on the screen --> <dimen name="navigation_bar_width">48dp</dimen> <!-- Height of the bottom navigation / system bar in car mode. -->
  15. BECOMING A MASTER WINDOW FITTER myView.setOnApplyWindowInsetsListener { view, insets ->

    // TODO handle insets return insets.consumeSystemWindowInsets() }
  16. BECOMING A MASTER WINDOW FITTER ViewCompat.setOnApplyWindowInsetsListener(view) { view, insets ->

    // TODO handle insets return insets.consumeSystemWindowInsets() }
  17. BECOMING A MASTER WINDOW FITTER class CustomLayout : LinearLayout {

    // yadda yadda override fun onApplyWindowInsets( insets: WindowInsets): WindowInsets { // TODO handle the insets return insets.consumeSystemWindowInsets() } }
  18. BECOMING A MASTER WINDOW FITTER class CustomLayout : LinearLayout {

    // yadda yadda override fun onApplyWindowInsets( insets: WindowInsets): WindowInsets { // TODO handle the insets return insets.consumeSystemWindowInsets() } }
  19. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 72 0 144 dispatchApplyWindowInsets()
  20. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 72 0 144 onApplyWindowInsets()
  21. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 0 0 144 onApplyWindowInsets() Consumes top
  22. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    WindowInsets.isConsumed() Left: Top: Right: Bottom: 0 0 0 144 false
  23. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    dispatchApplyWindowInsets() Left: Top: Right: Bottom: 0 0 0 144
  24. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 0 0 144 onApplyWindowInsets()
  25. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 0 0 144 WindowInsets.isConsumed() false
  26. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    dispatchApplyWindowInsets() Left: Top: Right: Bottom: 0 0 0 144
  27. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 0 0 144 onApplyWindowInsets()
  28. BECOMING A MASTER WINDOW FITTER Window Decor LinearLayout Child Child

    Left: Top: Right: Bottom: 0 0 0 0 WindowInsets.isConsumed() true
  29. BECOMING A MASTER WINDOW FITTER If you’re using DrawerLayout or

    CoordinatorLayout Use android:fitSystemWindows="true" on direct children which you want to be displayed behind the system bars
  30. BECOMING A MASTER WINDOW FITTER Avoid translucent system bars <item

    name="android:statusBarColor">#80000000 </item> <item name="android:navigationBarColor">#80000000 </item>
  31. BECOMING A MASTER WINDOW FITTER If you need to get

    access to the status bar size myView.setOnApplyWindowInsetsListener { view, insets -> val statusBarSize = insets.systemWindowInsetTop return insets }
  32. BECOMING A MASTER WINDOW FITTER Repeat once per week I

    will never store or retrieve the status bar size from resources ever again