Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Becoming a master window fitter (Droidcon Londo...

Chris Banes
October 27, 2017

Becoming a master window fitter (Droidcon London 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-lon/

Chris Banes

October 27, 2017
Tweet

More Decks by Chris Banes

Other Decks in Technology

Transcript

  1. Window Every view on screen is in a Window Key

    event handling Touch event handling Window decor Can think of it as a fancy ViewGroup
  2. S

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

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

    behavior of android:fitSystemWindows
  5. 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> >
  6. 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>
  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. <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
  13. 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. -->
  14. BECOMING A MASTER WINDOW FITTER myView.setOnApplyWindowInsetsListener { view, insets ->

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

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

    // yadda yadda override fun onApplyWindowInsets( insets: WindowInsets): WindowInsets { // TODO handle the 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 Window Decor LinearLayout Child Child

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

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

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

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

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

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

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

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

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

    Left: Top: Right: Bottom: 0 0 0 0 WindowInsets.isConsumed() true
  28. <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:statusBarBackground="@color/statusbar_scrim">e 
 <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true"

    android:fitsSystemWindows="true" android:src="@drawable/header" />
 <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:elevation="@dimen/elevation_toolbar" android:theme="@style/ThemeOverlay.AppCompat.Dark" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </android.support.design.widget.CoordinatorLayout>
  29. Need to tell CoodinatorLayout to lay out within the insets

    but we also want to stop the default padding android:fitSystemWindows="true" Erm… "
  30. If an OnApplyWindowInsetsListener is set, its onApplyWindowInsets method will be

    called instead of the View's own onApplyWindowInsets method. “
  31. 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
  32. BECOMING A MASTER WINDOW FITTER Avoid translucent system bars <item

    name="android:statusBarColor">#80000000 </item> <item name="android:navigationBarColor">#80000000 </item>
  33. 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 }
  34. BECOMING A MASTER WINDOW FITTER Repeat once per week I

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