Einhandgesten mit Compose


Ab Wear OS 7 (API-Level 37) können Nutzer mit einem Framework für Einhandgesten und einer API, die Teil von Compose für Wear OS ist, berührungslos mit Ihrer App interagieren.

Das Framework wird zunächst auf Pixel Watch-Geräten (Pixel Watch 3 und höher) unterstützt, ist aber für alle OEMs verfügbar. Wenn Sie diese API verwenden, wird die Unterstützung für Gesten in Ihrer App automatisch auf das gesamte Ökosystem skaliert, da die Hardwareunterstützung erweitert wird.

Damit Nutzer verfügbare Gesten leichter finden können, ohne die Benutzeroberfläche zu überladen, bietet das Wear OS-Framework animierte Gestenindikatoren. Diese visuellen Hinweise zeigen, wo eine Geste ausgeführt werden kann. Das System verwaltet automatisch die Anzeigefrequenz und die Häufigkeit der Stummschaltung gemäß den Nutzereinstellungen.

Unterstützte Gesten und Aktionen

Das Wear OS-Framework für Gesten unterstützt zwei Arten von Gesten:

  • Primäre Aktion (Doppel-Pinch): Wird der Hauptaktion auf einem Bildschirm zugeordnet, z. B. dem Annehmen eines Anrufs oder dem Umschalten der Medienwiedergabe.
  • Aktion zum Schließen (Drehen des Handgelenks): Wird der Rückwärtsnavigation, dem Schließen eines Dialogfelds oder dem Abbrechen einer Eingabeaufforderung zugeordnet.

Gesten in Compose konfigurieren

Die API für Einhandgesten kann zwar die Benutzeroberfläche verbessern, aber einige Hardware und OEMs unterstützen diese Gesten nicht. Wenn die API erkennt, dass Ihre App auf einem dieser nicht unterstützten Geräte ausgeführt wird, führt die Bibliothek automatisch keine Vorgänge aus, ohne die Standard-Touch-Interaktionen zu beeinträchtigen.

Wie bei Standardverhalten von Compose aktivieren Sie Einhandgesten für UI Elemente mit Modifikatoren. Sie konfigurieren die Gesten Ihrer App entsprechend der auszuführenden Aktion (primär oder schließen) und einer gestureId, um sie mit den Nutzereinstellungen auf Systemebene zu koordinieren, z. B. der Anzeigefrequenz und der Häufigkeit der Stummschaltung von Hinweisen. Diese Konfiguration wird durch Erstellen eines OneHandedGestureConfiguration Objekts ausgedrückt. Wir empfehlen, die Funktion rememberOneHandedGestureConfiguration zu verwenden, um es zu erstellen. In OneHandedGestureConfiguration können Sie auch die Priorität der Geste angeben.

Die Funktion rememberOneHandedGestureConfiguration verfolgt den Verlauf der Nutzerinteraktionen über mehrere Neukompositionen hinweg, ohne den Anwendungsstatus preiszugeben. Nachdem Ihre App die Konfiguration erstellt hat, sollte sie sie an Modifier.oneHandedGesture für die interaktive Komposition weitergeben.

Damit Nutzer verfügbare Gesten leichter finden können, bietet die Bibliothek die Methode OneHandedGestureClickIndicator. Diese Methode fungiert als Wrapper, der den zugrunde liegenden Inhalt ersetzt, um dem Nutzer mitzuteilen, dass eine Gestenaktion verfügbar ist.

Interaktive Komponenten

Wenn Sie Gesten für ein interaktives Steuerelement wie eine Schaltfläche aktivieren möchten, erstellen Sie eine Konfiguration, in der OneHandedGestureAction.Primary angegeben ist, und wenden Sie den Modifikator oneHandedGesture an. Übergeben Sie dieselbe MutableInteractionSource sowohl an das Steuerelement als auch an den Modifikator, damit bei Gestenereignissen visuelles Feedback auf das Steuerelement ausgegeben wird.

Wenn Sie den Gestenindikator aktivieren möchten, erstellen und speichern Sie eine Instanz von OneHandedGestureClickIndicatorState. Rufen Sie dann showIndicator im Callback onGestureAvailable auf, das vom Modifikator oneHandedGesture bereitgestellt wird, um das visuelle Feedback auszulösen. Dadurch wird dem System signalisiert, dass ein Ereignis für die Anzeige aufgetreten ist. Nach dem Aufruf ersetzt die Komponente ihren normalen Inhalt kurz durch eine Gestenanimation.

var isPlaying by remember { mutableStateOf(false) }
val onClick = { isPlaying = !isPlaying }

val gestureConfig = rememberOneHandedGestureConfiguration(
    action = OneHandedGestureAction.Primary
)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() }

Button(
    onClick = onClick,
    interactionSource = interactionSource,
    modifier = Modifier
        .fillMaxWidth()
        .oneHandedGesture(
            gestureConfiguration = gestureConfig,
            interactionSource = interactionSource,
            onGestureLabel = if (isPlaying) "pause" else "play",
            onGestureAvailable = { coroutineScope.launch { indicatorState.showIndicator() } },
            onGesture = onClick
        )
) {
    OneHandedGestureClickIndicator(
        gestureConfiguration = gestureConfig,
        state = indicatorState
    ) {
        Text(if (isPlaying) "Pause" else "Play", modifier = Modifier.fillMaxWidth())
    }
}

Scrollbare Container

Erstellen Sie für scrollbare Bildschirme oder Listen eine Konfiguration, in der OneHandedGestureAction.Primary angegeben ist, und wenden Sie den Modifikator oneHandedGesture auf den Container an. Rufen Sie dabei eine Scrollhilfe wie scrollDown auf.

Sie können die OneHandedGestureScrollIndicator verwenden, um visuelles Feedback für Scrollaktionen zu geben. Diese Komponente fungiert als Standard-Scrollindikator, der die Scrollposition anzeigt. Sie kann aber auch darauf hinweisen, dass für den Nutzer eine Scrollgeste verfügbar ist. Dieser Indikator wird in der Regel an den scrollIndicator Slot eines ScreenScaffold übergeben und mit dem Status eines scrollbaren Containers wie einer TransformingLazyColumn verknüpft. Außerdem wird ein OneHandedGestureScrollIndicatorState beobachtet, um die visuellen Übergänge zu verwalten.

Rufen Sie showIndicator für diesen Status auf, um das visuelle Feedback auszulösen. Dies erfolgt in der Regel im Callback onGestureAvailable des Modifikators oneHandedGesture. Nach dem Auslösen ersetzt der Indikator seinen visuellen Standardstatus vorübergehend durch eine Gestenanimationssequenz, um den Nutzer zu benachrichtigen.

val scrollState = rememberTransformingLazyColumnState()
val gestureConfig = rememberOneHandedGestureConfiguration(
    action = OneHandedGestureAction.Primary,
    priority = OneHandedGesturePriority.Scrollable
)
val indicatorState = remember(gestureConfig) { OneHandedGestureScrollIndicatorState() }
val coroutineScope = rememberCoroutineScope()

ScreenScaffold(
    scrollState = scrollState,
    scrollIndicator = {
        OneHandedGestureScrollIndicator(
            gestureConfiguration = gestureConfig,
            indicatorState = indicatorState,
            scrollState = scrollState,
            modifier = Modifier.align(Alignment.CenterEnd)
        )
    }
) { contentPadding ->
    TransformingLazyColumn(
        state = scrollState,
        contentPadding = contentPadding,
        modifier = Modifier
            .fillMaxSize()
            .oneHandedGesture(
                gestureConfiguration = gestureConfig,
                onGestureLabel = "scroll",
                onGestureAvailable = {
                    coroutineScope.launch { indicatorState.showIndicator() }
                },
                onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) }
            )
    ) {
        items(10) { index ->
            Text("Item $index", modifier = Modifier.padding(8.dp))
        }
    }
}

Mehrere Gesten kombinieren

Sie können sowohl eine Scrollgeste als auch eine Klickgeste mit derselben primären Aktion konfigurieren, indem Sie gesturePriority zum Objekt OneHandedGestureConfiguration hinzufügen:

  • OneHandedGesturePriority.Clickable (höchste Priorität): Weisen Sie diese Priorität interaktiven Steuerelementen zu, z. B. solchen mit dem Typ Button oder Card, damit sie Gesten erfassen, wenn sie auf dem Bildschirm sichtbar sind.
  • OneHandedGesturePriority.Scrollable (mittlere Priorität): Weisen Sie diese Priorität scrollbaren oder seitenbasierten Containern zu, damit sie untergeordneten Elementen mit Klickfunktion nachgeben, aber scrollen, wenn kein Steuerelement mit Klickfunktion sichtbar ist.
  • OneHandedGesturePriority.Unspecified (niedrigste Priorität): Eine nicht zugewiesene Priorität. Dies ist der Standardwert für eine Geste, für die keine priority festgelegt ist.

Wenn Sie priority = OneHandedGesturePriority.Clickable explizit für eine innere Schaltfläche und priority = OneHandedGesturePriority.Scrollable für die übergeordnete Liste festlegen, kann das System dieses Verhalten für die Gestenpriorität zeigen. Wenn der Nutzer die primäre Aktion mit der Einhandgeste auslöst, wird zuerst die Liste nach unten gescrollt, bis die Schaltfläche sichtbar ist. Anschließend wird die Klickaktion der Schaltfläche erfasst.

Gesten mit ADB testen und Fehler beheben

Sie können Einhandgesten auf einem physischen Gerät oder Emulator testen, ohne physische Handgelenkbewegungen auszuführen. Verwenden Sie dazu die Android Debug Bridge (adb) und den Systemdienst IWearGestureService.

Gestensimulation aktivieren

  1. Prüfen Sie, ob auf Ihrem Wear OS-Gerät Wear OS 7 (API-Level 37) oder höher ausgeführt wird.
  2. Wenn Sie auf einem physischen Gerät testen, das sich nicht am Handgelenk oder auf einem Ladegerät befindet, überschreiben Sie den Status des Sensors für das Tragen am Körper, damit das Gerät aktiv bleibt:
adb shell cmd sensorservice set-off-body-state 0

Gestenereignisse mit ADB auslösen

Führen Sie den folgenden ADB-Shell-Befehl aus, um die Geste Doppel-Pinch zu simulieren (die Primary-Aktion auf Pixel Watches):

adb shell cmd IWearGestureService gesture 1

Führen Sie den folgenden ADB-Shell-Befehl aus, um die Geste Drehen des Handgelenks zu simulieren (die Dismiss-Aktion auf Pixel Watches):

adb shell cmd IWearGestureService gesture 2

Tracking von Gestenhinweisen zurücksetzen

Das System verfolgt den Verlauf der Nutzerinteraktionen und zeigt schwebende Gestenhinweise basierend auf der globalen Anzeigefrequenz an (z. B. Immer oder Täglich). Wenn Sie Fehler bei den Gestenindikatoren Ihrer App beheben, setzen Sie diesen Verlauf zurück, damit Hinweise wieder für Ihr Paket angezeigt werden:

adb shell cmd IWearGestureService hint clear <your_package_name>

So setzen Sie den Status des Sensors für das Tragen am Körper zurück, wenn Sie mit dem Testen fertig sind:

adb shell cmd sensorservice reset-off-body-state

Zusätzliche Ressourcen

Designempfehlungen zur Verwendung von Einhandgesten finden Sie unter Einhandgesten.