Wrist gestures can enable quick, one-handed interactions with your app when a touch screen is inconvenient.
For example, a user can scroll through notifications with one hand while holding a cup of water with the other. Other use cases for wrist gestures include the following:
- In a jogging app, navigating through vertical screens that show the steps taken, time elapsed, and current pace
- In a travel app, scrolling through flight and gate information
- In a news app, scrolling through articles
To review the wrist gestures on a watch device, confirm that gestures are turned on by selecting Settings > Advanced features > Gestures > Wrist Gestures On. Then complete the Gestures tutorial on the watch by selecting Launch Tutorial.
Note: Shaking the wrist is the system-wide back or undo gesture and is not available for apps to customize.
Wrist gestures can be used in the following ways, as described in this guide:
- With a curved layout, which has predefined gesture actions
- By using key events directly to define new user actions
Each wrist gesture is mapped to an int
constant from the
KeyEvent
class, as shown in the following table:
Gesture | KeyEvent | Description |
---|---|---|
Flick wrist out |
KEYCODE_NAVIGATE_NEXT
|
This key code goes to the next item. |
Flick wrist in |
KEYCODE_NAVIGATE_PREVIOUS
|
This key code goes to the previous item. |
Use a curved layout to support wrist gestures
The
WearableRecyclerView
class provides a curved
layout for lists and automatically supports
wrist gestures. The class has predefined actions for occurrences of
wrist gestures when the view has focus. For information about using
the WearableRecyclerView
class, see Create lists on Wear OS. Also, see the
Best practices section of this guide.
Note: The WearableRecyclerView
class replaces a similar,
deprecated class in the Wearable Support Library.
Even if you use a WearableRecyclerView
, you might want to use
constants from the KeyEvent
class. The predefined actions can be overridden by subclassing the
WearableRecyclerView
and re-implementing the
onKeyDown()
callback. The behavior can be disabled entirely
by using setEnableGestureNavigation(false)
.
For more information, see
Handle keyboard actions.
Use key events directly
You can use key events outside of a
WearableRecyclerView
to trigger new actions in response to gesture
events. Importantly, these gesture events are recognized when a device is in
active mode, and they are delivered in the same way as all key events.
A class that relates to user interaction, such as a View
or an
Activity
, and that implements
KeyEvent.Callback
can listen to key events that relate to
wrist gestures just as it can listed to any other key event. The Android framework
calls the View
or Activity
that has
focus with the key events. For gestures, the onKeyDown()
method callback is called when gestures occur.
As an example, an app can override predefined actions in a View
or Activity
that implements KeyEvent.Callback
as follows:
Kotlin
class GesturesActivity : Activity() { /* KeyEvent.Callback */ override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_NAVIGATE_NEXT -> // Do something that advances a user View to the next item in an ordered list. moveToNextItem() KeyEvent.KEYCODE_NAVIGATE_PREVIOUS -> // Do something that advances a user View to the previous item in an ordered list. moveToPreviousItem() else -> { // If you did not handle it, let it be handled by the next possible element as determined // by the Activity. super.onKeyDown(keyCode, event) } } } /** Shows the next item in the custom list. */ private fun moveToNextItem(): Boolean { ... // Return true if handled successfully, otherwise return false. return false } /** Shows the previous item in the custom list. */ private fun moveToPreviousItem(): Boolean { ... // Return true if handled successfully, otherwise return false. return false } }
Java
public final class GesturesActivity extends Activity { @Override /* KeyEvent.Callback */ public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_NAVIGATE_NEXT: // Do something that advances a user View to the next item in an ordered list. return moveToNextItem(); case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS: // Do something that advances a user View to the previous item in an ordered list. return moveToPreviousItem(); } // If you did not handle it, let it be handled by the next possible element as determined by the Activity. return super.onKeyDown(keyCode, event); } /** Shows the next item in the custom list. */ private boolean moveToNextItem() { boolean handled = false; ... // Return true if handled successfully, otherwise return false. return handled; } /** Shows the previous item in the custom list. */ private boolean moveToPreviousItem() { boolean handled = false; ... // Return true if handled successfully, otherwise return false. return handled; } }
Best practices
- Review the
KeyEvent
andKeyEvent.Callback
pages for the delivery of key events to yourView
andActivity
. - Keep a consistent directional affordance: use "flick wrist out" for next and "flick wrist in" for previous.
- Have a touch parallel for a gesture.
- Provide visual feedback.
- Don't use a keycode to implement functionality that would be
counterintuitive to the rest of the system. For example, don't use
KEYCODE_NAVIGATE_NEXT
to cancel an action or to navigate the left-right axis with flicks. - Don't intercept the key events on elements that are not part of the user interface, such as views that are offscreen or partially covered. This is the same as for any key event.
- Don't reinterpret repeated flick gestures into your own novel gesture. This might conflict with the system's "shaking the wrist" gesture.
For a view to receive gesture key events, it must have focus; see
View.setFocusable()
.Because gestures are treated as key events, they trigger a transition out of "touch mode" that might do unexpected things. Since users may alternate between using touch and gestures, the
View::setFocusableInTouchmode()
method could be necessary. In some cases, it also could be necessary to usesetDescendantFocusability(FOCUS_BEFORE_DESCENDANTS)
so that when focus changes after a change to or from touch mode, your intended view gets the focus.- Use
requestFocus()
andclearFocus()
carefully:- When calling
requestFocus()
, make sure it's appropriate for the view to have focus. If the view is offscreen or is covered by another view, surprises can occur when gestures trigger callbacks. - The
clearFocus()
method initiates a focus search to find another suitable view. Depending on the view hierarchy, this search might require nontrivial computation. It can also end up assigning focus to a view you don’t expect to receive focus.
- When calling
Key events are delivered first to the view with focus in the view hierarchy. If the focused view does not handle the event—in other words, it returns
false
—the event is not delivered to the parent view, even if it can receive focus and has aKeyListener
. Rather, the event is delivered to the current activity holding the view hierarchy with focus.Therefore, it might be necessary to catch all events at the higher level, then pass relevant codes down. Alternatively, you might subclass the activity and override the
dispatchKeyEvent(KeyEvent event)
method to intercept keys when necessary or handle them when they are not handled at lower layers.