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. BECOMING A MASTER
    WINDOW FITTER
    @chrisbanes

    View Slide

  2. BECOMING A MASTER WINDOW FITTER
    chris.banes.me/dcnyc17

    View Slide

  3. What’s a window?

    View Slide

  4. View Slide

  5. 12:00

    View Slide

  6. 12:00

    View Slide

  7. 12:00

    View Slide

  8. 12:00
    Window

    View Slide

  9. 12:00
    Activity
    Window
    Activities have a Window
    Activity#getWindow()

    View Slide

  10. 12:00
    Window
    Activity
    Dialogs have a Window too
    Dialog#getWindow() Dialog

    View Slide

  11. 12:00
    Activity

    View Slide

  12. 12:00
    Activity Activity

    View Slide

  13. Why do I have to

    fit them?

    View Slide

  14. 12:00

    View Slide

  15. View Slide

  16. S

    View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. Inset

    View Slide

  24. Inset
    How the system tells you where the

    system ui currently is, or may later

    be displayed

    View Slide

  25. BECOMING A MASTER WINDOW FITTER
    History

    View Slide

  26. Pre-KitKat

    View Slide

  27. Pre-KitKat

    View Slide

  28. Pre-KitKat
    Window is placed within system bars

    View Slide

  29. Pre-KitKat
    Window is placed within system bars

    View Slide

  30. Pre-KitKat
    setSystemUiVisibility()
    Window is placed within system bars

    View Slide

  31. setSystemUiVisibility()

    View Slide

  32. SYSTEM_UI_FLAG_VISIBLE
    SYSTEM_UI_FLAG_LOW_PROFILE
    SYSTEM_UI_FLAG_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_FULLSCREEN
    SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    SYSTEM_UI_FLAG_IMMERSIVE
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    SYSTEM_UI_FLAG_LAYOUT_STABLE
    SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  33. SYSTEM_UI_FLAG_VISIBLE
    SYSTEM_UI_FLAG_LOW_PROFILE
    SYSTEM_UI_FLAG_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_FULLSCREEN
    SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    SYSTEM_UI_FLAG_IMMERSIVE
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    SYSTEM_UI_FLAG_LAYOUT_STABLE
    SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  34. SYSTEM_UI_FLAG_VISIBLE
    SYSTEM_UI_FLAG_LOW_PROFILE
    SYSTEM_UI_FLAG_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_FULLSCREEN
    SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    SYSTEM_UI_FLAG_IMMERSIVE
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    SYSTEM_UI_FLAG_LAYOUT_STABLE
    SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  35. SYSTEM_UI_FLAG_VISIBLE
    SYSTEM_UI_FLAG_LOW_PROFILE
    SYSTEM_UI_FLAG_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_FULLSCREEN
    SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    SYSTEM_UI_FLAG_IMMERSIVE
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    SYSTEM_UI_FLAG_LAYOUT_STABLE
    SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  36. SYSTEM_UI_FLAG_VISIBLE
    SYSTEM_UI_FLAG_LOW_PROFILE
    SYSTEM_UI_FLAG_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_FULLSCREEN
    SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    SYSTEM_UI_FLAG_IMMERSIVE
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    SYSTEM_UI_FLAG_LAYOUT_STABLE
    SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  37. SYSTEM_UI_FLAG_VISIBLE
    SYSTEM_UI_FLAG_LOW_PROFILE
    SYSTEM_UI_FLAG_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_FULLSCREEN
    SYSTEM_UI_FLAG_IMMERSIVE_STICKY
    SYSTEM_UI_FLAG_IMMERSIVE
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    SYSTEM_UI_FLAG_LAYOUT_STABLE
    SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    chris.banes.me/systemuihelper

    View Slide

  38. indow lag
    W F
    ransform
    T s
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    or

    View Slide

  39. W F
    T s
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    or

    View Slide

  40. myView.systemUiVisibility =
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  41. myView.systemUiVisibility =
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  42. KitKat

    View Slide

  43. KitKat

    View Slide

  44. KitKat
    Translucent system bars

    View Slide

  45. KitKat
    Translucent system bars

    View Slide

  46. KitKat
    Translucent system bars
    android:windowTranslucentStatus
    android:windowTranslucentNavigation

    View Slide

  47. Translucent system bars
    WTFs are implied
    KitKat

    View Slide

  48. Translucent system bars
    WTFs are implied
    System bar backgrounds are drawn
    by WindowManager
    KitKat

    View Slide

  49. Lollipop

    View Slide

  50. Lollipop

    View Slide

  51. Lollipop
    android:windowDrawsSystemBarBackgrounds
    System bar backgrounds are placed
    in Window

    View Slide

  52. View Slide

  53. android:navigationBarColor
    getWindow().setNavigationBarColor()

    View Slide

  54. android:statusBarColor
    getWindow().setStatusBarColor()

    View Slide

  55. Lollipop
    WTFs are not implied

    View Slide

  56. Lollipop
    They take precedence over custom
    colors
    Translucent system bars
    They disable
    windowDrawsSystemBarBackgrounds

    View Slide

  57. BECOMING A MASTER WINDOW FITTER
    How can you handle this?

    View Slide

  58. android:fitSystemWindows="true"

    View Slide

  59. android:fitSystemWindows="true"
    Uses padding to move child views within insets
    Default View.java behavior:

    View Slide

  60. 12:00

    View Slide

  61. 12:00
    android:fitSystemWindows="true"

    View Slide

  62. 12:00
    android:fitSystemWindows="true"
    systemUiVisibility =
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  63. 12:00
    android:fitSystemWindows="true"
    systemUiVisibility =
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    View Slide

  64. 12:00
    android:fitSystemWindows="true"
    systemUiVisibility =
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    Padding
    Padding

    View Slide

  65. Rule of thumb
    You probably want to avoid the default

    behavior of android:fitSystemWindows

    View Slide

  66. But the docs say to use it?!

    View Slide

  67. But the docs say to use it?!
    DrawerLayout
    CoordinatorLayout
    AppBarLayout
    CollapsingToolbarLayout

    View Slide

  68. DrawerLayout
    Sets the WTFs for you on API
    21+
    12:00

    View Slide

  69. 12:00
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end" />
    layout_height="match_parent"
    layout_width="match_parent">




    >

    View Slide

  70. 12:00
    >
    fitSystemWindows="true"
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end" />
    layout_height="match_parent"
    layout_width="match_parent">




    View Slide

  71. layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end" />
    layout_height="match_parent"
    layout_width="match_parent">



    12:00
    fitSystemWindows="true">

    View Slide

  72. DrawerLayout
    Sets the WTFs for you on API
    21+
    12:00
    Use fitSystemWindows as a
    signal from children

    View Slide

  73. 12:00
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    fitSystemWindows="true">
    />

    View Slide

  74. 12:00
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    fitSystemWindows="true">
    />

    View Slide

  75. layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    fitSystemWindows="true">
    />
    fitSystemWindows="true"
    12:00

    View Slide

  76. layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    fitSystemWindows="true">
    />
    fitSystemWindows="true"
    12:00

    View Slide

  77. layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    fitSystemWindows="true">
    />
    fitSystemWindows="true"
    12:00

    View Slide

  78. fitSystemWindows="true">
    />
    fitSystemWindows="true"
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    12:00

    View Slide

  79. fitSystemWindows="true">
    />
    fitSystemWindows="true"
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent">



    12:00

    View Slide

  80. fitSystemWindows="true">
    />
    fitSystemWindows="true"
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent"



    >
    12:00

    View Slide

  81. fitSystemWindows="true">
    />
    fitSystemWindows="true"
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent"



    fitSystemWindows="true">
    12:00

    View Slide

  82. fitSystemWindows="true">
    />
    fitSystemWindows="true"
    layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent"



    12:00
    fitSystemWindows="true">

    View Slide

  83. layout_height="match_parent"
    layout_width="match_parent"
    layout_height="match_parent"
    layout_width="match_parent"

    layout_gravity="end"
    layout_height="match_parent"
    layout_width="match_parent"



    12:00
    fitSystemWindows="true">
    fitSystemWindows="true">
    />
    fitSystemWindows="true"

    View Slide

  84. CoordinatorLayout
    + AppBarLayout + CollapsingToolbarLayout

    View Slide

  85. 12:00
    Sets the WTFs for you on API
    21+
    Use fitSystemWindows as a
    signal from children
    CoordinatorLayout
    + AppBarLayout + CollapsingToolbarLayout

    View Slide

  86. layout_height="match_parent"
    layout_width="match_parent">
    layout_height="match_parent"
    layout_width="wrap_content" />

    id="@+id/poster" />



    View Slide

  87. layout_height="match_parent"
    layout_width="match_parent"
    fitSystemWindows="true">
    layout_height="match_parent"
    layout_width="wrap_content"
    fitSystemWindows="true" />
    fitSystemWindows="true">
    id="@+id/poster"
    fitSystemWindows="true" />



    View Slide

  88. BECOMING A MASTER WINDOW FITTER
    Manual handling

    View Slide

  89. BECOMING A MASTER WINDOW FITTER
    What not to do…

    View Slide

  90. Fixed dimension for status bar
    What not to do…

    View Slide


  91. 24dp

    View Slide


  92. 24dp

    res/values-v23

    25dp

    res/values

    View Slide

  93. What if the status bar size changes?

    View Slide


  94. View Slide

  95. View Slide

  96. What happens if devices have
    different status bar sizes?

    View Slide

  97. 53dp
    24dp

    View Slide

  98. Retrieve internal system

    resources
    What not to do…

    View Slide



  99. 192dp

    192dp

    60%

    240

    48dip
    64dip

    24dp

    48dp

    48dp

    48dp

    frameworks/base/core/res/values/dimens.xml

    View Slide

  100. frameworks/base/core/res/values/dimens.xml


    192dp

    192dp

    60%

    240

    48dip
    64dip

    24dp

    48dp

    48dp

    48dp

    View Slide

  101. var resourceId = resources.getIdentifier(

    "status_bar_height", "dimen", "android")

    return resources.getDimensionPixelSize(resourceId)

    View Slide

  102. What happens if the internal

    resource name changes?

    View Slide

  103. View Slide

  104. What if the device does
    not let you draw behind
    the status bar?

    View Slide

  105. View Slide

  106. View Slide

  107. View Slide

  108. BECOMING A MASTER WINDOW FITTER
    The supported way: WindowInsets

    View Slide

  109. BECOMING A MASTER WINDOW FITTER
    WindowInsets
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom()

    View Slide

  110. BECOMING A MASTER WINDOW FITTER
    WindowInsets
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom()
    Compat

    View Slide

  111. 12:00
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom()

    View Slide

  112. 12:00
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom()
    0

    View Slide

  113. 12:00
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom()
    72
    0

    View Slide

  114. 12:00
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom()
    72
    0
    0

    View Slide

  115. 12:00
    getSystemWindowInsetLeft()
    getSystemWindowInsetTop()
    getSystemWindowInsetRight()
    getSystemWindowInsetBottom() 144
    72
    0
    0

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  120. BECOMING A MASTER WINDOW FITTER
    WindowInsets will be passed down until

    it has been consumed

    View Slide

  121. BECOMING A MASTER WINDOW FITTER
    Window Decor
    LinearLayout
    Child Child
    dispatchApplyWindowInsets()

    View Slide

  122. BECOMING A MASTER WINDOW FITTER
    Window Decor
    dispatchApplyWindowInsets()
    LinearLayout
    Child Child

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  133. BECOMING A MASTER WINDOW FITTER
    TL;DW

    View Slide

  134. 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

    View Slide

  135. BECOMING A MASTER WINDOW FITTER
    Avoid translucent system bars
    #80000000
    #80000000

    View Slide

  136. 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
    }

    View Slide

  137. BECOMING A MASTER WINDOW FITTER
    Repeat once per week
    I will never store or retrieve the status bar size from
    resources ever again

    View Slide

  138. Over and out…
    @chrisbanes

    View Slide