Gestenverwaltung: Umgang mit Gestenkonflikten. Teil 3

Eine Übersetzung des Artikels wurde vor dem Start eines fortgeschrittenen und grundlegenden Android-Entwicklungskurses vorbereitet .



Dies ist der dritte Artikel in einer Reihe über Gestensteuerung. Sie können sich mit den Übersetzungen des ersten und zweiten Teils vertraut machen, wenn Sie sie verpasst haben.

In früheren Artikeln haben wir uns mit dem Thema des Füllens des Bildschirmbereichs von Kante zu Kante befasst. In diesem Artikel erfahren Sie, wie Sie mit Konflikten umgehen, die zwischen den Gesten Ihrer Anwendung und den neuen Systemgesten in Android 10 auftreten .

Was verstehen wir unter Gestenkonflikten? Schauen wir uns ein Beispiel an. Angenommen, wir haben einen Musik-Player, mit dem der Benutzer durch Ziehen und Ablegen durch das aktuelle Lied scrollen kann SeekBar.



Leider befindet es SeekBarsich zu nahe am Bereich der Geste, zum Startbildschirm zurückzukehren, wodurch die Geste des schnellen Wechsels zur vorherigen Anwendung (QuickSwitch) beginnt, was dem Benutzer Unannehmlichkeiten bereitet.

Dasselbe kann an jedem Rand des Bildschirms passieren, an dem sich Gestenbereiche befinden. Es gibt viele gängige Beispiele, die Konflikte verursachen können, z. B .: Navigationsschubladen ( DrawerLayout), Karussells ( ViewPager ), Schieberegler ( SeekBar), Wischen in Listen.

Was uns zu der Frage bringt, wie wir das beheben können. Um diese Frage zu erleichtern, haben wir ein Flussdiagramm erstellt, das Ihnen eine situationsabhängige Antwort bietet.


Eine PDF-Version des Flussdiagramms finden Sie hier .

Ich hoffe, dass die Fragen keiner Erklärung bedürfen, aber für alle Fälle werde ich sie alle durchgehen:

1. Muss die Anwendung die Navigations- und Statusleisten ausblenden?


Die erste Frage ist, ob der Hauptanwendungsfall für Ihre Anwendung das Ausblenden der Navigations- und / oder Statusleisten erfordert . Mit Ausblenden meinen wir, dass diese Systemfenster überhaupt nicht sichtbar sind. Dies bedeutet nicht , dass Sie das Konzept „Edge to Edge“ oder ähnliches in Ihrer Anwendung implementiert haben.

Mögliche Gründe, diese Frage mit „Ja“ zu beantworten:


Häufige Beispiele für Anwendungen, die diese Frage mit „Ja“ beantworten sollten, sind Spiele, Videoplayer, Fotobetrachter und Zeichenanwendungen.

2. Das Hauptszenario für die Verwendung der Benutzeroberfläche umfasst Wischen im / um den Gestenbereich?


Diese Frage stellt fest, ob Ihre Benutzeroberfläche Elemente in / neben Gestenbereichen (sowohl "Zurück" als auch "Zuhause") enthält, die der Benutzer wischen muss.

Spiele sagen normalerweise ja, weil das so ist

  • Die Steuerelemente auf dem Bildschirm befinden sich normalerweise am linken / rechten Rand und am unteren Bildschirmrand.
  • In einigen Spielen müssen Sie über Elemente streichen, die sich an einer beliebigen Stelle auf dem Bildschirm befinden können.

Neben Spielen gibt es häufig Beispiele für Benutzeroberflächen, die Ja sagen sollten:

  • Beschneiden von Fotos auf der Benutzeroberfläche, bei denen sich ziehbare Rahmen auch am linken und rechten Bildschirmrand befinden.
  • Eine Zeichenanwendung, bei der der Benutzer auf einer Leinwand zeichnen kann, die den gesamten Bildschirm abdeckt.

3. Häufig verwendete Ansicht im / um den Gestenbereich?


Ich hoffe, das ist eine ziemlich einfache Frage. Dies schließt auch Ansichten ein, die den Bereich der Gesten abdecken und sich dann beispielsweise auf einen großen Teil des Bildschirms DrawerLayoutoder einen großen Teil erstrecken ViewPager.

4. Ansicht beinhaltet Wischen / Ziehen?


Wir ändern die Taktik ein wenig und beginnen, einzelne Ansichten zu betrachten. Für jede der Ansichten, auf die Sie die dritte Frage bejaht haben, machen wir eine kleine Klarstellung: Sollte der Benutzer sie streichen / ziehen?

Es gibt viele Beispiele, bei denen Sie mit "Ja" SeekBars, " BottomSheet" oder sogar PopupMenu(zum Öffnen müssen Sie ziehen) antworten müssen.

5. Befindet sich die Ansicht vollständig / allgemein unter den Gestenbereichen?


Anhand der vierten Frage klären wir nun, ob sich die Ansicht vollständig oder hauptsächlich im Gestenbereich befindet.

Wenn sich Ihre Ansicht in einem scrollbaren Container wie diesem befindet RecyclerView, denken Sie etwas anders über diese Frage nach: Fällt die vollständig / grundsätzlich erweiterte Ansicht in allen Bildlaufpositionen unter den Gestenbereich ? Wenn der Benutzer die Ansicht unter dem Gestenbereich scrollen kann, müssen Sie nichts tun.

In der obigen Abbildung sehen Sie möglicherweise ein Karussell im Vollbildmodus ( ViewPager) als Beispiel für eine negative Antwort und fragen sich, warum dieser Fall nicht behandelt werden muss. Dies liegt an der Tatsache, dass die Bereiche der Gesten links / rechts im 20dpVergleich zur Breite der Ansicht relativ klein sind (Standard: jeweils). TypischDie Breite des Bildschirms Ihres Telefons im Hochformat beträgt ~ 360 dp, sodass ~ 320 dp freier Speicherplatz verbleiben, auf dem der Benutzer keine Schwierigkeiten hat (dies sind fast 90% des Bildschirms). Selbst mit internen Rändern / Einkerbungen kann der Benutzer bequem durch das Karussell scrollen.

6. Überlappt der Ansichtsrand alle erforderlichen Gestenbereiche?


Die letzte Frage verdeutlicht, ob sich die Ansicht unter einem der erforderlichen Gestenbereiche befindet. Wenn Sie sich an unseren vorherigen Artikel erinnern , werden Sie sich daran erinnern, dass die obligatorischen Bereiche von Systemgesten Bereiche des Bildschirms sind, in denen Systemgesten immer Vorrang haben.

In Android 10 gibt es nur einen obligatorischen Gestenbereich am unteren Bildschirmrand, über den der Benutzer entweder nach Hause zurückkehren oder seine neuesten Anwendungen öffnen kann. Dies kann sich in zukünftigen Versionen der Plattform ändern, aber jetzt müssen wir nur noch mit der Ansicht am unteren Bildschirmrand arbeiten.

Typische Beispiele sind:

  • Modeless BottomSheet , wie sie in einen kleinen zusammenzubrechen neigen drag-and-drop Ansicht am unteren Rand des Bildschirms.
  • Ein horizontal scrollendes Karussell am unteren Bildschirmrand, beispielsweise eine Schnittstelle mit Aufklebern.

Nachdem wir die Fragen geklärt haben, hoffen wir, dass Sie zu einer der Lösungen kommen können. Schauen wir uns die einzelnen Fragen genauer an.

Keine Konflikte zu behandeln


Beginnen wir mit der einfachsten „Lösung“, tun Sie einfach nichts !

Natürlich gibt es vielleicht noch Raum für Optimierungen, die Sie vornehmen können (siehe Abschnitt unten), aber zum Glück gibt es keine ernsthaften Probleme bei der Verwendung der Anwendung mit aktiviertem Gesten-Navigationsmodus.

Wenn der Zeitplan Sie hierher gebracht hat, Sie aber dennoch das Gefühl haben, dass ein Problem vorliegt, lassen Sie es uns bitte wissen . Vielleicht haben wir etwas verpasst.

Verschieben der Ansicht aus Gestenbereichen


Wie wir aus unserem vorherigen Artikel erfahren haben , sind Einfügungen vorhanden, die Ihrer Anwendung mitteilen, wo sich die Systemgestenzonen auf dem Bildschirm befinden. Eine der Methoden, mit denen wir Gestenkonflikte lösen können, besteht darin, widersprüchliche Ansichten aus Gestenbereichen zu verschieben. Dies ist besonders wichtig für die Ansicht am unteren Bildschirmrand, da dieser Bereich die Zone für obligatorische Gesten darstellt und Anwendungen die Gestenausschluss-APIs dort nicht verwenden können.

Schauen wir uns noch einmal ein Beispiel an. Wir haben eine Musik-Player-Oberfläche , die wir oben gezeigt haben. Es enthält SeekBaram unteren Bildschirmrand, sodass der Benutzer durch das Lied scrollen kann.


UI-Musikplayer mit einer SeekBar am unteren Bildschirmrand

Wenn ein Benutzer jedoch versucht, einen Titel zu überspringen, geschieht


Folgendes : Aufzeichnen einer Systemgeste, die mit SeekBar in Konflikt steht

Dies liegt daran, dass der untere Bereich der Geste die SeekBar überlappt, sodass die Home-Back-Geste Vorrang hat. Hier ist die Visualisierung der Gestenzonen:



Eine einfache Lösung


Die einfachste Lösung besteht darin, einen zusätzlichen Einzug / Rand hinzuzufügen, damit sich die SeekBar vom Gestenbereich nach oben bewegt.



Etwa so: Wenn wir in diesem Beispiel die SeekBar ziehen, werden Sie feststellen, dass wir die Heimkehrgeste nicht mehr aktivieren:


SeekBar steht nicht mehr in Konflikt mit der Geste des unteren Systems.

Um dies zu implementieren, müssen wir die neuen Systemgesten-Insets verwenden, die in API 29 und der Jetpack Core-Bibliothek v1.2.0 (derzeit in Alpha ) verfügbar sind . Im Beispiel erhöhen wir den unteren Einzug SeekBar, um ihn an den Wert des unteren Gesteneinsatzes anzupassen :

ViewCompat.setOnApplyWindowInsetsListener(seekBar) { view, insets ->
     //      view ,    system gesture insets
    view.updatePadding(
        bottom = insets.systemGestureInsets.bottom
    )
    insets
}

Wenn Sie lernen möchten, wie Sie die Arbeit vereinfachen WindowInsetskönnen, lesen Sie unseren anderen Artikel zu diesem Thema:

WindowInsets - Listener-Layout

Weitere Maßnahmen


An diesem Punkt können Sie entscheiden, dass die Arbeit bereits erledigt ist, und für einige Layouts ist dies möglicherweise die endgültige Lösung. In unserem Beispiel ist die Benutzeroberfläche jedoch visuell zurückgegangen, und es ist viel Platz verloren gegangen SeekBar. Anstatt einfach die Ansicht nach oben zu verschieben, können wir stattdessen das Layout neu gestalten, um Platzverlust zu vermeiden:


SeekBarVerschoben an den oberen Rand des Wiedergabefelds.

Hier haben wir uns SeekBarganz außerhalb des Gestenbereichs an den oberen Rand des Wiedergabefelds bewegt . Dies bedeutet, dass wir die Höhe der Platte nicht mehr künstlich erhöhen müssen, um sie aufzunehmen SeekBar.

, . « ».

API


In unserem früheren Artikel haben wir erwähnt , dass „Anwendungen haben die Fähigkeit , System - Gesten für bestimmte Teile des Bildschirms auszuschließen . Anwendungen verwenden dazu die APIs zum Ausschließen von Gesten, die erstmals in Android 10 angezeigt wurden.

Das System bietet zwei verschiedene Funktionen zum Ausschließen von Gestenbereichen: View.setSystemGestureExclusionRects()und Window.setSystemGestureExclusionRects(). Was Sie verwenden sollten, hängt von der Anwendung ab: Wenn Sie Android verwenden View, bevorzugt das System die Ansichts-API, andernfalls verwenden Sie die WindowAPI.

Der Hauptunterschied zwischen den beiden APIs besteht darin, dass die Fenster-APIerwartet, dass sich Rechtecke im Koordinatenraum des Fensters befinden. Wenn Sie die Ansicht verwenden, arbeiten Sie normalerweise stattdessen im Ansichtskoordinatenraum. Die Ansichts-API kümmert sich um die Transformation zwischen Koordinatenräumen, dh Sie müssen nur den Inhalt der Ansicht berücksichtigen.

Schauen wir uns ein Beispiel an. Wir werden wieder unser Beispiel eines Musik-Players verwenden, der SeekBarsich über die gesamte Breite des Bildschirms befindet. Wir haben den Konflikt SeekBarmit der Geste behoben , zum Startbildschirm im vorherigen Abschnitt zurückzukehren, aber wir haben immer noch linke und rechte Bereiche von Gesten, um die wir uns kümmern müssen.

Mal sehen, was passiert, wenn ein Benutzer versucht, ein Lied zu überspringen, wenn der „Schieberegler“SeekBar(kreisförmiges Ziehen) befindet sich in der Nähe einer der Kanten:


SeekBar hat einen Konflikt mit dem hinteren Gestenbereich.

Da sich der Schieberegler unter dem rechten Gestenbereich befindet, geht das System davon aus, dass der Benutzer versucht, mit der Geste zurückzukehren, und zeigt daher den hinteren Pfeil an. Dies ist für Benutzer unpraktisch, da sie im Moment wahrscheinlich nicht zurückkehren wollten. Wir können dies mithilfe der oben genannten Gestenausschluss-APIs beheben, um die Ränder des Schiebereglers auszuschließen.

Gestenausnahme-APIs werden normalerweise von zwei Stellen aus aufgerufen: onDraw()beim Rendern Ihrer Ansicht und onLayout()ansonsten. Ihre Ansicht geht vorbeiList<Rect>enthält alle auszuschließenden Rechtecke. Wie bereits erwähnt, müssen sich diese Rechtecke in einem eigenen Ansichtskoordinatensystem befinden.

Normalerweise erstellen Sie eine Funktion wie diese, die von onLayout()und / oder aufgerufen wird onDraw():

private val gestureExclusionRects = mutableListOf<Rect>()
private fun updateGestureExclusion() {
   //   ,      Android 10+
   if (Build.VERSION.SDK_INT < 29) return
  // -,     
   gestureExclusionRects.clear()
      //   ,    .  SeekBar    .
   thumb?.also { t ->
       gestureExclusionRects += t.copyBounds()
   }
   //            view,       ,     
   // ,       
   systemGestureExclusionRects = gestureExclusionRects
}

Ein vollständiges Beispiel finden Sie hier .

Nachdem wir dies hinzugefügt haben, funktioniert das Zurückspulen in der Nähe der Kanten wie erwartet:


SeekBar arbeitet im Bereich der hinteren Geste.

Hinweis zum obigen Beispiel. SeekBarerledigt dies bereits automatisch für Sie in Android 10, sodass Sie es nicht selbst tun müssen. Hier machen wir es nur als Beispiel, um Ihnen den allgemeinen Überblick zu zeigen.

Einschränkungen


Obwohl die Gestenausschluss-APIs die perfekte Lösung für die Lösung aller Gestenkonflikte zu sein scheinen, ist dies tatsächlich nicht der Fall. Mithilfe der Gestenausschluss-APIs erklären Sie, dass die Geste Ihrer Anwendung wichtiger ist als Systemrückgabeaktionen. Dies ist eine starke Aussage, daher ist diese API als Notausgang konzipiert, wenn Sie nichts anderes tun können.

Mit den Gestenausschluss-APIs erklären Sie, dass die Geste Ihrer Anwendung wichtiger ist als die Systemaktion des Backtracking.

Da das von der API bereitgestellte Verhalten eine komfortable Benutzererfahrung beeinträchtigen kann, beschränkt das System seine Verwendung: Anwendungen können nur bis zu 200 dp pro Kante ausschließen.

Hier sind einige häufig gestellte Fragen, die Entwickler haben, wenn sie dies hören:

Warum ist die Einschränkung notwendig? Ich hoffe, die obige Erklärung hat Sie bereits zu einem Grund geführt. Wir glauben, dass es für den Benutzer sehr wichtig ist, konsequent vom Seitenwischen zurückgehen zu können. Konsistent im gesamten Gerät, nicht eine Anwendung. Diese Einschränkung mag zu restriktiv erscheinen, aber nur eine Anwendung, mit Ausnahme des gesamten Bildschirmrandes, reicht aus, um dem Benutzer Unannehmlichkeiten zu bereiten, die entweder zum Entfernen der Anwendung oder zu etwas Radikalerem führen.

Mit anderen Worten, das Navigationssystem sollte immer konsistent und einfach zu bedienen sein.

Warum 200dp?Das Argument für 200dp ist ziemlich einfach. Wie bereits erwähnt, sollen die Gestenausschluss-APIs als letzter Ausweg verwendet werden. Daher wurde diese Grenze als Vielfaches mehrerer wichtiger Berührungsziele berechnet. Die empfohlene Mindestgröße für ein sensorisches Ziel beträgt 48dp0,4 sensorische Ziele × 48dp = 192dp. Fügen Sie weitere Einrückungen hinzu und wir erhalten den Wert 200dp.

Was ist, wenn ich mehr als 200 dp pro Kante ausschließen muss? Das System schließt nur die niedrigsten 200dp aus, die Sie angefordert haben.


Das System ermöglicht eine Anforderung für eine Gesamthöhe von 200 dp, die vom unteren Rand meiner Ansicht außerhalb des Bildschirms ausgeht. Wird

dies als Grenze angesehen?Nein, das System berücksichtigt nur ausgeschlossene Rechtecke, die sich auf dem Bildschirm befinden. Wenn sich die Ansicht teilweise auf dem Bildschirm befindet, wird nur der sichtbare Teil des angeforderten Rechtecks ​​berücksichtigt.

Tauchen Sie ein in den nächsten Beitrag


Vielleicht fragen Sie sich nach dem Lesen, warum wir die rechte Seite des Flussdiagramms nicht berücksichtigt haben. Die folgenden Lösungen wurden speziell für Anwendungen entwickelt, die beim Rendern den gesamten Bildschirm verwenden. Wir werden im nächsten Teil darüber sprechen.






All Articles