Fokusziele für Gruppen

Mit dem Modifikator focusGroup können Sie mehrere Fokusziele gruppieren. Mit der Gruppierung können Sie Focushierarchien verwalten, logische UI-Bereiche (z. B. Symbolleisten, Dialogaktionen oder Kartenlisten) isolieren und abfangen, wenn der Fokus in einen Bereich eintritt oder ihn verlässt.

Fokusgruppenkonzepte: Ein- und Ausstieg sowie Standarddurchlauf

Eine Fokusgruppe legt eine zusammenhängende Navigationsgrenze um ihre untergeordneten Fokusziele fest. Um zu verstehen, wie sich der Fokus über diese Grenze hinweg bewegt, sind zwei wichtige Konzepte erforderlich:

  • Enter: Das Ereignis, das auftritt, wenn der Fokus von einem Element außerhalb der Fokusgruppe zu einem Element innerhalb der Gruppe wechselt.
  • Exit: Das Ereignis, das auftritt, wenn der Fokus das letzte interne Element der Gruppe verlässt und zu einem externen Fokusziel wechselt.
Diagramm mit Ein- und Ausstiegsvorgängen über die Grenze einer Fokusgruppe hinweg.
Abbildung 1. Fokusgruppe mit Ein- und Ausstieg-Ereignissen.

Standardverhalten beim Durchlaufen des Fokus

Wenn eine Fokusgruppe standardmäßig von außen fokussiert wird, gilt Folgendes:

  1. Eingabe der Gruppe: Compose wählt automatisch ein internes Fokus-Ziel für untergeordnete Elemente aus. Bei der eindimensionalen Navigation (Tab) wird das erste untergeordnete Element in der Reihenfolge der visuellen Darstellung ausgewählt. Bei der zweidimensionalen Navigation (Pfeiltasten oder Steuerkreuz) wird das untergeordnete Element ausgewählt, das der eingehenden Richtung geometrisch am nächsten ist.
  2. Innerhalb der Gruppe navigieren: Durch wiederholtes Drücken der Navigationstaste wird der Fokus zwischen den untergeordneten Fokus-Zielen innerhalb der Gruppe verschoben.
  3. Gruppe verlassen: Wenn der Nutzer das letzte Fokus-Ziel in der Gruppe passiert hat, wird die Gruppe mit dem nächsten Tastendruck verlassen und der Fokus wird auf das benachbarte Fokus-Ziel außerhalb der Gruppe verschoben.

Umleitung von Fokuseinträgen anpassen

In vielen UI-Designs ist das Standardziel für den Einstieg (z. B. das erste untergeordnete Element in der Reihenfolge der Darstellung) nicht die logischste oder bequemste Wahl für den Nutzer.

Intention und Anwendungsfälle

Erwägen Sie ein Bestätigungsdialogfeld mit einem „Abbrechen“-Button auf der linken Seite und einem primären „Bestätigen“-Button auf der rechten Seite. Wenn der Nutzer von einem Formularfeld aus in die Schaltflächenleiste navigiert:

  • Standardverhalten: Der Fokus würde auf „Abbrechen“ liegen, da dies die erste Schaltfläche in der visuellen Reihenfolge ist.
  • Gewünschtes Verhalten: Da „Bestätigen“ die primäre empfohlene Maßnahme ist, sollte der Fokus direkt auf „Bestätigen“ liegen, wenn die Schaltflächengruppe aus einem früheren Element in der Fokusreihenfolge aufgerufen wird. Der Nutzer sollte aber trotzdem Nach links drücken können, um bei Bedarf „Abbrechen“ auszuwählen.
Standardmäßiger Fokuseintrag in einer Schaltflächengruppe.
Abbildung 2. Die Schaltflächen Abbrechen und Bestätigen mit dem Standardziel für den Fokus.

Weitere häufige Anwendungsfälle:

  • Der Fokus wird direkt auf den aktuell ausgewählten Tab verschoben, wenn eine TabRow eingegeben wird.
  • Der Fokus wird direkt auf das Steuerelement „Wiedergabe / Pause“ verschoben, wenn eine Medienwiedergabeleiste aufgerufen wird.

Implementierung mit onEnter und FocusRequester

So leiten Sie den Fokus beim Aufrufen einer Fokusgruppe um:

  1. Wenden Sie den Modifikator focusGroup auf den übergeordneten Container an, der die zugehörigen Fokusziele enthält.
  2. Wenden Sie den Modifikator focusProperties auf den Container an und konfigurieren Sie den Callback onEnter.
  3. Rufen Sie im onEnter-Callback (mit dem Bereich FocusEnterExitScope) requestFocus() für ein FocusRequester auf, das dem bevorzugten Standarduntergeordneten Element mit focusRequester zugeordnet ist.

val defaultChildRequester = remember { FocusRequester() }

Row(
    modifier = Modifier
        .focusGroup()
        .focusProperties {
            // Intercept entry into this group and redirect focus to the primary action
            onEnter = {
                defaultChildRequester.requestFocus()
            }
        }
) {
    Button(onClick = { /* Cancel action */ }) {
        Text("Cancel")
    }
    Button(
        onClick = { /* Confirm action */ },
        modifier = Modifier.focusRequester(defaultChildRequester)
    ) {
        Text("Confirm (Default)")
    }
}

Innerhalb von onEnter und onExit können Sie auch cancelFocusChange() aufrufen, um Fokusübergänge über die Grenze hinweg vollständig zu blockieren, oder requestedFocusDirection prüfen, um das Routing basierend darauf anzupassen, ob der Nutzer mit Tab, Nach unten oder Nach links navigiert hat.

Scrollbare Container sind standardmäßig Fokusgruppen.

In Jetpack Compose fungieren scrollbare Container wie LazyRow, LazyColumn und LazyVerticalGrid automatisch als Fokusgruppen. Sie müssen den focusGroup-Modifikator nicht explizit anwenden, um onEnter- oder onExit-Callbacks festzulegen.