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

Handling focus in 2024

Tiphaine
April 26, 2024

Handling focus in 2024

Tiphaine

April 26, 2024
Tweet

More Decks by Tiphaine

Other Decks in Technology

Transcript

  1. Handling focus in 2024 Supporting different navigation ways with Compose

    Tiphaine (DeNA) AndroidMakers by droidcon 2024
  2. 2 • Android developer @ DeNA (Pococha) • DroidKaigi volunteer

    staff • Accessibility & privacy 🔥 Hello! I am Tiphaine ɹ tahia910
  3. 5

  4. 6

  5. 9 • Alternative way to interact with your phone without

    using the touch screen • Move through the UI one element at a time Focus Navigation
  6. Focus Navigation 10 Focus • Current position in the UI

    • Can only interact with this element • Element will have some visual changes when focused
  7. Focus in Android world 11 • Accessibility focus vs Navigation

    focus • Navigation focus is sometimes
 called Keyboard Navigation • Both focuses are Focus Navigation variants 😵💫 Focus
  8. Don’t touch the screen 👋 👀 Don’t see the screen

    13 Navigation Focus Accessibility Focus
  9. 15 Navigation Focus Accessibility Focus Only actionable elements All elements

    focusable attribute focusable and importantForAccessibility attributes
  10. 16 Navigation Focus Accessibility Focus Only actionable elements All elements

    focusable attribute focusable and importantForAccessibility attributes Keyboard, d-pad, switch… Screen readers like TalkBack
  11. 17 Devices used for navigating • Hardware keyboard (tablets, Chromebooks)

    • On-screen keyboard (soft input keyboard) PhotoɿSergi Kabrera
  12. 18 • TV remote controller • Smartwatches rotating bezel •

    Game controller • Switch Access RefɿGoogle Devices used for navigating
  13. 19 focusable and importantForAccessibility attributes Changeable indicator Only one default

    indicator Only actionable elements All elements focusable attribute focusable and importantForAccessibility attributes Keyboard, d-pad, switch… Screen readers like TalkBack Navigation Focus Accessibility Focus
  14. 24 Directional navigation • Using a D-pad (directional pad) 


    or arrow keys on a keyboard • Two-dimensional nav • Focus mainly goes up, down, left, or right
  15. 25 Tab navigation • Using Switch Access or “tab” key

    on a keyboard. • One-dimensional nav (forward or backward) • Focus follows the order in which elements appear in the layout (top left to bottom right)
  16. • Do all actions • Navigate without keyboard traps •

    See which element is being focused • Skip non-interactive elements 29 Users must be able to:
  17. 30 • Make actionable elements focusable, especially if you don’t

    use OnClickListener // Focusable with all focuses android:focusable=“true" Ensure clickability
  18. myView.setOnLongClickListener { // Do something true } // Set this

    for any listener 
 // except setOnClickListener⚠ timelineItem.isFocusable = true 32
  19. 34 • Skip non-interactive elements • Wait for an action

    to initiate changes // Skip the view android:focusable=“false” Meet the user’s expectations
  20. 35 • Make sure the focus is always moveable •

    Verify the Return key allows to move away • Double check your WebViews, scrolls 
 and drop-down lists Avoid keyboard traps
  21. 36 • Ensure the flow is logical and consistent Facilitate

    navigation // Tab navigation
 android:nextFocusForward=“@id/...” // Directional navigation // Can also set up, down and left android:nextFocusRight=“@id/...”
  22. 37 Facilitate navigation RefɿAndroid Developers // Available from API 26+

    android:keyboardNavigationCluster • Group elements with clusters 1 2 3 4 10 11 12 Main content Bottom nav bar Top tabs
  23. 38 • Prevent the on-screen keyboard to appear on top

    of the text field being focused Optimize text input <activity ... // Or use “adjustPan” android:windowSoftInputMode="adjustResize" /> RefɿAndroid Developers, Lua Software ?
  24. 39 • Request the focus when the start is obvious

    Optimize text input // Available from API 26+ // Won’t show on-screen keyboard <EditText ... android:focusedByDefault="true"/>
  25. 41 • Ensure the focus is visible • Customize the

    indicator color and/or shape when necessary Don’t let the user get lost RefɿGoogle Play Store app in 2021
  26. Customize the indicator: whole app Customize the indicator 42 <!--

    themes.xml --> <style name=“AppTheme" ...> ... <item name="colorControlHighlight">...</item> </style>
  27. Customize the indicator: single View ᶃ 43 <!-- outline.xml -->

    <shape ... android:shape="rectangle"> ... <stroke android:width="4dp" android:color=“@color/black” /> </shape> Customize the indicator: single View Customize the indicator
  28. 44 <!-- focus_selector.xml --> <selector ...> </selector> Customize the indicator:

    single View ᶄ Customize the indicator: single View Customize the indicator <item android:drawable=“@drawable/outline” 
 android:state_focused="true" /> <item android:drawable="@color/transparent"/>
  29. 46 Give some time • Stop motions when focused •

    Get recommended time out to set elements that disappear
  30. // Can combine with other flags: // FLAG_CONTENT_ICONS, FLAG_CONTENT_TEXT AccessibilityManager.FLAG_CONTENT_CONTROLS,

    // The original timeout you planned defaultTimeOut, 48 // Available from API 29+ a11yManager?.getRecommendedTimeoutMillis( ) RefɿAung Kyaw Paing, Suchi Bansal a11yManager
  31. 51 Focusable, no warning, 
 but still not working :(

    true } myView.isFocusable = true view.performClick() myView.setOnTouchListener { view, event -> // Do something with touch event
  32. 52 • performClick() just executes the code set in a

    separate OnClickListener • “ClickableViewAccessibility” warning is for accessibility focus Handle gesture-based actions
  33. 53 • Nav focus can't even trigger OnTouchListener 😢 •

    Need a different way to detect input events Handle gesture-based actions 👉 Listen to KeyEvents instead
  34. myView.setOnKeyListener { view, keyCode, event -> 54 } // Choose

    which one(s) you want to support KeyEvent.KEYCODE_ENTER, ... -> { // Do something true } else -> false when (keyCode) { RefɿAndroid Developers }
  35. myView.setOnKeyListener { view, keyCode, event -> } when (keyCode) {

    } 55 Parse duplicate events ⚠ when (keyCode) { ... } // Handle the event only once if (event.action != KeyEvent.ACTION_UP) { return@setOnKeyListener false }
  36. 56 override fun onKeyUp(keyCode: Int, event: KeyEvent?)
 : Boolean {

    } Use this instead for custom views return when (keyCode) { KeyEvent.KEYCODE_ENTER -> performClick() else -> ... }
  37. 61 • Focus follows the declaration order of Composables •

    Priority goes to elements
 inside the same level Tab Navigation flow // Level ᶃ // Level ᶄ // Level ᶄ Column { Row { Row { } } } ... ...
  38. 63 Row { Column { } Column { } }

    CustomButton("1") CustomButton("2") CustomButton("3") CustomButton("4")
  39. Modifier.focusable() 66 • Need to provide a focus indicator and

    handle the displaying logic 😭 Make the element focusable
  40. 67 Modifier var color by remember { .focusable() mutableStateOf(transparent) }

    // Modifier of your Composable Set focusable as the last Modifier ⚠
  41. 68 Modifier .border(5.dp, indicatorColor) .onFocusChanged { focusState -> } color

    = if (focusState.isFocused) { black } else { transparent } var color by remember { ...} .focusable() 🎉
  42. 69 Modifier.clickable { // Do something } • Composable is

    focusable by default when using the default click listener ✅ Handle click actions
  43. Modifier.combinedClickable( ) 71 onClick = { }, // Not triggered

    😢 // Mandatory & non-nullable onLongClick = { },
  44. 73 Listen to KeyEvents Modifier.onPreviewKeyEvent {} Modifier.onKeyEvent {} // Parent

    callback is invoked first // Start from children callback
  45. 74 Modifier.onKeyEvent { keyEvent -> } when (keyEvent.key) { Modifier.onKeyEvent

    Key.Enter -> { // Do something true } else -> false }
  46. 75 // Parse duplicate if (keyEvent.type != KeyEventType.KeyUp) { return@onPreviewKeyEvent

    false } when (keyEvent.key) { } when (keyEvent.key) { ... } Modifier.onKeyEvent { keyEvent -> } Modifier.onKeyEvent
  47. 77 RefɿAung Kyaw Paing a11yManager?.calculateRecommendedTimeoutMillis( ) a11yManager Get the recommended

    time out originalTimeoutMillis = defaultTimeOut, containsIcons = false, containsText = false, containsControls = true, // Returns a Long value
  48. 79 Change the flow - Tab Navigation Change the flow

    Modifier.focusProperties { } next = Modifier .focusProperties { } previous = ... ... • FocusProperties with FocusRequester
  49. Modifier 81 val (first, second) = remember { FocusRequester.createRefs() }

    first // First Composable // Second Composable Modifier.focusRequester( ) ) second .focusRequester(
  50. 82 val (first, second) = remember { FocusRequester.createRefs() } Modifier

    .focusProperties { } second next = first) .focusRequester(
  51. 83 // Won’t be used The top most Modifier wins

    ⚠ Modifier .focusProperties { } .focusProperties { previous = second } next = second
  52. 87 keyboardActions = KeyboardActions( onNext = { } ), ),

    val focusManager = LocalFocusManager.current TextField(... keyboardOptions = KeyboardOptions(... ) focusManager.moveFocus( ) FocusDirection.Down
  53. 88 Move the focus • Go wherever you want (as

    long as it’s focusable) Move the focus - the other way val requester = remember { FocusRequester() } requester.requestFocus()
  54. 89 Button( onClick = { ) { ... } TextField(...

    } ) val requester = remember { FocusRequester() } Modifier.focusRequester(requester) requester.requestFocus()
  55. 91 • Plug it to your phone • Tap any

    key to make the indicator appear • 🎉 • Can also use other tools with D-pad 🎮 Use a hardware keyboard
  56. 92 1⃣ Set up an emulator OR 2⃣ Enable device

    mirroring Use your laptop keyboard
  57. 93 • D-pad navigation is enabled by default with pre-set

    hardware profiles • For new profiles, select the option when creating the device 1⃣ Set up an emulator
  58. 94

  59. 95

  60. 96

  61. 97

  62. 98

  63. 99 • Use a real device through AndroidStudio • Preferences

    → Tools → Device mirroring 2⃣ Enable device mirroring
  64. 100

  65. 102 • Focus in Compose - Android Developers official documentation

    • Android App Development: Accessibility - Renato Iwashima References