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

Going edge-to-edge with Gesture Navigation

Going edge-to-edge with Gesture Navigation

To aid developers to create immersive experiences, Android Q adds new system navigation models to allow apps to take over more of the user’s screen.

This talk will look at how apps should work with the new gesture navigation, and ways to mitigate gesture conflicts. We’ll look at how to update apps to go edge-to-edge, drawing behind the system bars to create a immersive experience for users. Finally we’ll go through ways to make the WindowInsets APIs easier to use.

Chris Banes

July 02, 2019
Tweet

More Decks by Chris Banes

Other Decks in Programming

Transcript

  1. Which nav will Android use in the future? 3-button mode

    is required for Android devices Gestural nav may be offered as a polished, immersive experience, and can additionally be offered out-of-box
  2. Why is this so important for apps? Gaining an edge

    on user experience given market desire for beautiful UI Handling conflicts with gestures for optimal UX
  3. Making your app nav-ready 1. Make your UI edge-to-edge 2.

    Leverage insets for better UI 3. Override system gestures
  4. System will help From Q onwards, the system is responsible

    for protecting system buttons/handles
  5. System will help From Q onwards, the system is responsible

    for protecting system buttons/handles
  6. System will help From Q onwards, the system is responsible

    for protecting system buttons/handles Protection can be in the form of dynamic color adaptation
  7. System will help From Q onwards, the system is responsible

    for protecting system buttons/handles Or the system can draw a translucent scrim Shown in button mode when targetSdkVersion >= 29
  8. Going immersive Avoid overlaps with system UI Request to be

    laid out fullscreen Change system bar colors 1 2 3
  9. view.systemUiVisibility = // Tells the system that you wish to

    be laid out // as if the navigation bar was hidden View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or // Tells the system that you wish to be laid out at // the most extreme scenario of any other flags View.SYSTEM_UI_FLAG_LAYOUT_STABLE Request to be laid out fullscreen 2
  10. view.systemUiVisibility = // Tells the system that you wish to

    be laid out // as if the navigation bar was hidden View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or // Optional, if we want you be laid out fullscreen, // behind the status bar View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or // Tells the system that you wish to be laid out at // the most extreme scenario of any other flags View.SYSTEM_UI_FLAG_LAYOUT_STABLE Request to be laid out fullscreen 2
  11. What are insets? A collection of values which tell you

    how much to inset (move) content in by Which content to move, depends on the inset type and its purpose
  12. I N S E T S System window Tells you

    where the system- ui is being displayed over your app Use to move clickable views away from edges
  13. I N S E T S System window Tells you

    where the system- ui is being displayed over your app Use to move clickable views away from edges
  14. System window I N S E T S Tells you

    where the system- ui is being displayed over your app Use to move clickable views away from edges
  15. System window I N S E T S Tells you

    where the system- ui is being displayed over your app Use to move clickable views away from edges
  16. System gesture Represents the areas of the window where system

    gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges New in Q I N S E T S
  17. System gesture New in Q I N S E T

    S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges
  18. System gesture New in Q I N S E T

    S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges
  19. System gesture New in Q I N S E T

    S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges
  20. System gesture New in Q I N S E T

    S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges
  21. Mandatory 
 system gesture Subset of system gesture insets Defines

    areas which cannot be overridden by apps In Q, currently only used by the home gesture zone I N S E T S New in Q
  22. System window insets Use to move clickable views away from

    edges System gesture insets Use to move draggable views away from edges Mandatory system gesture insets Use to check what gesture areas can not be excluded
  23. ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> // Do something with the

    insets // return the insets insets } Tip: always use the ViewCompat method
  24. VC.setOnApplyWindowInsetsListener(bottomBar) { v, insets -> v.updatePadding( bottom = bottomPad +

    insets.systemWindowInsets.bottom ) insets } val bottomPad = bottomBar.bottomPadding // 32dp
  25. VC.setOnApplyWindowInsetsListener(bottomBar) { v, insets -> v.updatePadding( bottom = bottomPad +

    insets.systemWindowInsets.bottom ) insets } val bottomPad = bottomBar.bottomPadding
  26. data class InitialPadding(
 val left: Int, val top: Int, val

    right: Int, val bottom: Int
 ) private fun recordInitialPaddingForView(view: View) = ... fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding) -> Unit) { // Create a snapshot of the view's padding state val initialPadding = recordInitialPaddingForView(this) // Set an OnApplyWindowInsetsListener which proxies to the given // lambda, passing in the original padding state ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> f(v, insets, initialPadding) // Always return the insets, so that children can also use them insets } }
  27. data class InitialPadding(
 val left: Int, val top: Int, val

    right: Int, val bottom: Int
 ) private fun recordInitialPaddingForView(view: View) = ... fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding) -> Unit) { // Create a snapshot of the view's padding state val initialPadding = recordInitialPaddingForView(this) // Set an OnApplyWindowInsetsListener which proxies to the given // lambda, passing in the original padding state ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> f(v, insets, initialPadding) // Always return the insets, so that children can also use them insets } }
  28. val right: Int, val bottom: Int
 ) private fun recordInitialPaddingForView(view:

    View) = ... fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding) -> Unit) { // Create a snapshot of the view's padding state val initialPadding = recordInitialPaddingForView(this) // Set an OnApplyWindowInsetsListener which proxies to the given // lambda, passing in the original padding state ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> f(v, insets, initialPadding) // Always return the insets, so that children can also use them insets } }
  29. @BindingAdapter( "paddingLeftSystemWindowInsets", "paddingTopSystemWindowInsets", "paddingRightSystemWindowInsets", "paddingBottomSystemWindowInsets", requireAll = false ) fun

    applySystemWindows( view: View, applyLeft: Boolean, applyTop: Boolean, applyRight: Boolean, applyBottom: Boolean ) { view.doOnApplyWindowInsets { view, insets, padding -> val left = if (applyLeft) insets.systemWindowInsetLeft else val top = if (applyTop) insets.systemWindowInsetTop else 0
  30. What conflicts will apps face? Swiping in from the edges

    will go back or re-activate system gestures when in immersive mode. Any horizontally-draggable content that relies on gestures in the back zone Examples: sliders, seek bars, drag handles, etc.
  31. What conflicts will apps face? Swiping in from the edges

    will go back or re-activate system gestures when in immersive mode. Any horizontally-draggable content that relies on gestures in the back zone Examples: sliders, seek bars, drag handles, etc.
  32. Overriding gestures New API available to you for opting out

    a small part of the edge for app use Should be used extremely sparingly and tied to a draggable handle or visual affordance New in Q
  33. List<Rect> exclusionRects; public void onLayout(...) { // Update rect bounds

    and the exclusionRects list setSystemGestureExclusionRects(exclusionRects); } CustomView.java
  34. List<Rect> exclusionRects; public void onDraw(Canvas canvas) { // Update rect

    bounds and the exclusionRects list setSystemGestureExclusionRects(exclusionRects); } CustomView.java
  35. Restrictions on overriding gestures Apps can only opt out 200

    dp of each edge If apps opt out of more, only the bottom 200 dp will be overridden for the app Requested
  36. Restrictions on overriding gestures Apps can only opt out 200

    dp of each edge If apps opt out of more, only the bottom 200 dp will be overridden for the app Honored Requested
  37. Non-sticky immersive mode Apps can only opt out 200 dp

    of each edge from bringing in system bars and activating gestures
  38. Non-sticky immersive mode Apps can only opt out 200 dp

    of each edge from bringing in system bars and activating gestures
  39. Non-sticky immersive mode Apps can only opt out 200 dp

    of each edge from bringing in system bars and activating gestures
  40. Non-sticky immersive mode Apps can only opt out 200 dp

    of each edge from bringing in system bars and activating gestures
  41. Non-sticky immersive mode Apps can only opt out 200 dp

    of each edge from bringing in system bars and activating gestures
  42. Sticky immersive mode By updating, apps can opt the entire

    edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides
  43. Sticky immersive mode By updating, apps can opt the entire

    edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides
  44. Sticky immersive mode By updating, apps can opt the entire

    edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides
  45. Sticky immersive mode By updating, apps can opt the entire

    edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides
  46. Sticky immersive mode By updating, apps can opt the entire

    edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides
  47. Sticky immersive mode By updating, apps can opt the entire

    edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides
  48. Sticky immersive mode Useful for single-purpose UIs that want to

    keep the user from accidentally going back Examples: games, drawing apps, etc.
  49. Scrolling views Content should be drawn behind the navigation bar

    When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S
  50. Scrolling views Content should be drawn behind the navigation bar

    When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S
  51. Scrolling views Content should be drawn behind the navigation bar

    When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S
  52. Scrolling views Content should be drawn behind the navigation bar

    When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S
  53. Scrolling views Common way to achieve this is through padding

    We’ll pad our scrolling view using the system window insets S C E N A R I O S
  54. recyclerview.setOnApplyWindowInsetsListener { v, insets -> // Set the bottom padding

    so that the content bottom // is above the nav bar (y-axis) v.updatePadding(bottom = insets.systemWindowInsetBottom) // return the insets insets }
  55. Bottom sheets For bottom sheets which collapse, ensure enough content

    is visible above the navigation bar to be dragged S C E N A R I O S
  56. Bottom sheets For bottom sheets which collapse, ensure enough content

    is visible above the navigation bar to be dragged Use the system gesture insets to increase the peeking content height S C E N A R I O S
  57. Bottom sheets For bottom sheets which collapse, ensure enough content

    is visible above the navigation bar to be dragged Use the system gesture insets to increase the peeking content height S C E N A R I O S
  58. // Record the original peek height val origPeekHeight = behavior.peekHeight


    
 view.setOnApplyWindowInsetsListener { v, insets -> val gestureInsets = insets.getSystemGestureInsets() // Update the peek height so that it is above the navigation bar behavior.peekHeight = gestureInsets.bottom + origPeekHeight // return the insets insets }
  59. Bottom sheets Remember, users can always swipe to go home

    Highlight draggable content to guide user S C E N A R I O S
  60. Bottom navigation For bottom tabbed navigation, pad the bottom content

    in by the system window inset S C E N A R I O S
  61. Bottom navigation For bottom tabbed navigation, pad the bottom content

    in by the system window inset S C E N A R I O S
  62. Landscape S C E N A R I O S

    If you have content that starts near a vertical edge, think about padding all content in horizontally Make sure to test apps with button navigation too
  63. rootView.setOnApplyWindowInsetsListener { v, insets -> // Set the left/right padding

    so that the content // is above the nav bar (y-axis) v.updatePadding( left = insets.systemWindowInsetLeft, right = insets.systemWindowInsetRight ) // return the insets insets }
  64. Navigation drawers New swipe-when-peeking behavior in Q Beta 5 Works

    with all versions of DrawerLayout Update to DrawerLayout 1.1.0-alpha02 for best experience S C E N A R I O S
  65. TL;DR G E S T U R E N A

    V I G A T I O N Users will expect apps to work with all navigation modes Create immersive UIs by going edge-to-edge Proactively update Jetpack and MDC libraries for automatic support Only use gesture exclusion APIs if you really need them Let us know about troublesome scenarios in your apps