Some Wear OS devices support rotary input, such as a rotating side button (RSB). When the user turns the button, your app's current view should scroll up or down.
If you are using ScrollView, ListView, HorizontalScrollView, or WearableRecyclerView in your app, then your app's view gets rotary input support by default. If you are using custom views other than those listed, or if you want to manually handle rotary input events, see Adding Custom Rotary Input Scrolling.
Refer to the following related resources:
Focus best practices
To respond to rotary input events, your scroll view must be in focus. In most cases, this happens automatically; your scrolling view becomes focused immediately after you create your Activity. However, there are situations where you need to manually handle focus of your app's view, such as:
- If your scrollable view is attached after Activity.onCreate() (for example, if you wait for a network request to finish before building your UI), then you must call requestFocus after attaching it.
- If your scrollable view is initially
INVISIBLE or GONE, you must call
requestFocus
when you set it to VISIBLE. - If your Activity contains multiple scrollable views (for example, if you are using nested scrolling) you need to choose one to be focused (usually via the <requestFocus /> tag). Nested scrolling is not currently supported for RSB scrolling.
- If your UI contains some other view that steals focus when the user interacts with it
(these are rare; the most common example is an
InputText
), you need to provide the user with some way to restore focus to the scrollable view if it loses focus. Usually this is done by listening for taps on the scrollable view and callingrequestFocus
in response.
Add custom rotary input scrolling
If your scrollable view doesn't natively support rotary input scrolling, or if you want to
do something other than scrolling in response to rotary input events (zoom in/out,
turn dials, etc.), you can use the
RotaryEncoder
methods in the
Wearable Support Library.
The following code snippet shows how to use RotaryEncoder
to add custom scrolling
in your app's view:
Kotlin
myView.setOnGenericMotionListener(View.OnGenericMotionListener { v, ev -> if (ev.action == MotionEvent.ACTION_SCROLL && RotaryEncoder.isFromRotaryEncoder(ev)) { // Don't forget the negation here val delta = -RotaryEncoder.getRotaryAxisValue(ev) * RotaryEncoder.getScaledScrollFactor(context) // Swap these axes if you want to do horizontal scrolling instead v.scrollBy(0, Math.round(delta)) return@OnGenericMotionListener true } false })
Java
myView.setOnGenericMotionListener(new View.OnGenericMotionListener() { @Override public boolean onGenericMotion(View v, MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_SCROLL && RotaryEncoder.isFromRotaryEncoder(ev)) { // Don't forget the negation here float delta = -RotaryEncoder.getRotaryAxisValue(ev) * RotaryEncoder.getScaledScrollFactor( getContext()); // Swap these axes if you want to do horizontal scrolling instead v.scrollBy(0, Math.round(delta)); return true; } return false; } });
Test rotary input button on the emulator
You can use the Android Emulator to simulate rotary input scrolling on a Wear device. Launch your Wear app on the emulator when you run your project, or drag an APK file onto the emulator to install it.
To test the rotary input on the emulator:
- From the SDK manager, use the SDK tools tab to get Android Emulator 26.0.3 or higher.
- Create an AVD (Android Virtual Device) with API 25.
In Studio, select Tools>Android>AVD Manager. Create a new Wear device with API 25.
- Run the emulator from the Android Studio.
- Try rotary input scrolling.
Click the overflow button (three dots at the bottom of the emulator toolbar). Click the Rotary input tab in the new window to open the rotary input interface.
The following video shows rotary input in the emulator:
Focus behavior tips
- Since Android 9 (API level 28), views no longer implicitly receive focus, and must do so explicitly instead.
- A scrollable view should be registered as focusable using both
android:focusable="true"
andandroid:focusableInTouchMode="true"
. - By default, launching an Activity or even tapping on a view does not focus it (even if it is
focusable). To achieve this behavior, the view must use the
<requestFocus />
tag or manually callView.requestFocus()
. - Rotary input events are only sent to the focused view. These events do not bubble up the view
hierarchy. If there is no focused view, or if the focused view returns
false
fromView.onGenericMotionEvent
, then (and only then) the event is sent toActivity.onGenericMotionEvent
.