Fokusverhalten ändern

Manchmal ist es erforderlich, das Standardfokusverhalten der Elemente außer Kraft zu setzen. auf deinem Bildschirm. Sie können beispielsweise zusammensetzbare Funktionen gruppieren, verhindern, Sie legen den Fokus auf eine bestimmte zusammensetzbare Funktion, bitten Sie sich explizit auf eine Funktion, Fokus erfassen oder freigeben oder Fokus auf Ein- oder Ausstieg weiterleiten Dieses wird beschrieben, wie du den Fokus ändern kannst, wenn die Standardeinstellungen nicht deinen Einstellungen entsprechen. die Sie brauchen.

Kohärente Navigation mit Fokusgruppen

Manchmal errät Jetpack Compose nicht sofort das richtige nächste Element für Navigation mit Tabs, insbesondere wenn komplexe übergeordnete Composables wie Tabs und ins Spiel.

Während die Fokussuche normalerweise der Deklarationsreihenfolge von Composables folgt, ist dies in manchen Fällen nicht möglich, z. B. wenn einer der Composables im ist ein horizontal scrollbares Element, das nicht vollständig sichtbar ist. Dies wird in im Beispiel unten.

Jetpack Compose kann den Fokus auf das nächste Element legen, das dem Anfang wie unten gezeigt, anstatt den Pfad zu wählen, den Sie für Navigation in eine Richtung:

<ph type="x-smartling-placeholder">
</ph> Animation einer App mit einer horizontalen Navigationsleiste oben und einer Liste von Elementen darunter.
Abbildung 1: Animation einer App mit einer horizontalen Navigationsleiste oben und einer Liste von Elementen darunter

In diesem Beispiel ist klar, dass die Entwickelnden nicht die Absicht hatten, sich auf wechseln Sie vom Tab Schokolade zum ersten Bild unten und zurück zum Tab Pastries (Süßgebäck). Stattdessen sollte der Fokus so lange auf den Tabs bleiben, bis die den letzten Tab und konzentrieren Sie sich dann auf den inneren Inhalt:

<ph type="x-smartling-placeholder">
</ph> Animation einer App mit einer horizontalen Navigationsleiste oben und einer Liste von Elementen darunter.
Abbildung 2: Animation einer App mit einer horizontalen Navigationsleiste oben und einer Liste von Elementen darunter

In Situationen, in denen es wichtig ist, dass eine Gruppe von zusammensetzbaren Funktionen sequenziell, wie in der Tab-Zeile aus dem vorherigen Beispiel, das Composable in einem übergeordneten Element mit dem focusGroup()-Modifikator:

LazyVerticalGrid(columns = GridCells.Fixed(4)) {
    item(span = { GridItemSpan(maxLineSpan) }) {
        Row(modifier = Modifier.focusGroup()) {
            FilterChipA()
            FilterChipB()
            FilterChipC()
        }
    }
    items(chocolates) {
        SweetsCard(sweets = it)
    }
}

Die bidirektionale Navigation sucht nach der am besten zusammensetzbaren Funktion für die gegebene Richtung: wenn ein Element aus einer anderen Gruppe näher an einem nicht vollständig sichtbaren in der aktuellen Gruppe ausgewählt haben, wird das nächstgelegene ausgewählt. Um dies zu vermeiden können Sie den focusGroup()-Modifikator anwenden.

Mit FocusGroup erscheint eine ganze Gruppe in Bezug auf den Fokus wie eine einzelne Entität. aber die Gruppe selbst hat nicht den Fokus, sondern das nächstgelegene untergeordnete konzentrieren Sie sich stattdessen. So kann die Navigation zu den nicht vollständig sichtbaren Element vor dem Verlassen der Gruppe.

In diesem Fall werden die drei Instanzen von FilterChip bevorzugt vor dem SweetsCard Elemente, auch wenn die SweetsCards für das Nutzer und einige FilterChip sind möglicherweise ausgeblendet. Das liegt daran, dass der Der focusGroup-Modifikator weist den Fokusmanager an, die Reihenfolge anzupassen, in der die Elemente fokussiert sind, damit die Navigation einfacher und kohärenter mit der Benutzeroberfläche ist.

Wenn FilterChipC ohne den Modifikator focusGroup nicht sichtbar war, fokussieren Sie würde sie als Letztes aufnehmen. Durch das Hinzufügen eines solchen Modifizierers nur sichtbar, aber auch direkt nach FilterChipB in den Fokus rücken, was die Nutzer erwarten.

Zusammensetzbare fokussierbare Funktion erstellen

Einige zusammensetzbare Funktionen sind standardmäßig fokussierbar, z. B. eine Schaltfläche oder eine zusammensetzbare Funktion mit den daran angehängten clickable-Modifikator. Wenn Sie eine spezifische für eine zusammensetzbare Funktion verwenden, verwenden Sie den focusable-Modifikator:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

Eine zusammensetzbare Funktion unfokussierbar machen

Es kann Situationen geben, in denen einige Ihrer Elemente nicht berücksichtigt werden sollten. im Fokus. In diesen seltenen Fällen können Sie die canFocus property um ein Composable von der Fokussierung auszuschließen.

var checked by remember { mutableStateOf(false) }

Switch(
    checked = checked,
    onCheckedChange = { checked = it },
    // Prevent component from being focused
    modifier = Modifier
        .focusProperties { canFocus = false }
)

Tastaturfokus mit FocusRequester anfordern

In einigen Fällen können Sie den Fokus explizit als Antwort auf eine der Nutzerinteraktion. Sie können z. B. einen Nutzer fragen, ob er ein Formular ausfüllt und auf „Ja“ sollten Sie das erste Feld dieses Formats.

Verknüpfen Sie zuerst ein FocusRequester-Objekt mit dem zusammensetzbare Funktion, auf die Sie den Tastaturfokus verschieben möchten. Im folgenden Code Snippet ist, wird ein FocusRequester-Objekt mit einem TextField verknüpft, indem ein mit dem Namen Modifier.focusRequester:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

Sie können die Methode requestFocus von FocusRequester aufrufen, um tatsächliche Fokusanfragen zu senden. Sie sollten diese Methode außerhalb eines Composable-Kontexts aufrufen (andernfalls wird es bei jeder Neuzusammensetzung neu ausgeführt). Das folgende Snippet zeigt, wie das System aufgefordert wird, den Tastaturfokus zu verschieben, wenn die Schaltfläche angeklickt:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

Button(onClick = { focusRequester.requestFocus() }) {
    Text("Request focus on TextField")
}

Fokus aufnehmen und loslassen

Sie können den Fokus nutzen, um Ihre Nutzer dazu anzuleiten, Ihrer App die richtigen Daten zur Verfügung zu stellen. bestimmte Aufgaben ausführen muss, z. B. eine gültige E-Mail-Adresse oder Telefonnummer anfordern. Nummer. Auch wenn Fehlerstatus Ihre Nutzenden darüber informieren, was passiert, benötigt das Feld mit fehlerhaften Informationen, um konzentriert zu bleiben, behoben.

Um den Fokus zu erfassen, können Sie die Methode captureFocus() aufrufen. Lassen Sie es anschließend mit der Methode freeFocus() los, wie im folgenden Beispiel gezeigt: Beispiel:

val textField = FocusRequester()

TextField(
    value = text,
    onValueChange = {
        text = it

        if (it.length > 3) {
            textField.captureFocus()
        } else {
            textField.freeFocus()
        }
    },
    modifier = Modifier.focusRequester(textField)
)

Vorrang von Fokusmodifikatoren

Modifiers sind Elemente, die nur ein untergeordnetes Element haben. Wenn Sie die Warteschlange umschließt jedes Modifier-Element links (oder oben) das Modifier-Element, das auf nach rechts (oder darunter). Das bedeutet, dass das zweite Modifier-Element in einem die erste, sodass bei der Deklaration von zwei focusProperties nur die oberste funktioniert, da sich die folgenden im obersten befinden.

Zur Verdeutlichung des Konzepts sehen Sie den folgenden Code:

Modifier
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

In diesem Fall zeigt die focusProperties, die item2 als rechten Fokus angibt, nicht verwendet werden, da im vorigen Abschnitt beschrieben. Somit ist item1 der Wert einer verwendet.

Mit diesem Ansatz können Eltern das Verhalten auch auf die Standardeinstellungen zurücksetzen, mit FocusRequester.Default:

Modifier
    .focusProperties { right = Default }
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

Das übergeordnete Element muss nicht Teil derselben Modifikatorkette sein. Ein Elternteil zusammensetzbare Funktion kann die Fokuseigenschaft einer untergeordneten zusammensetzbaren Funktion überschreiben. Beispiel: Sehen Sie sich folgendes FancyButton an, das die Schaltfläche nicht fokussierbar macht:

@Composable
fun FancyButton(modifier: Modifier = Modifier) {
    Row(modifier.focusProperties { canFocus = false }) {
        Text("Click me")
        Button(onClick = { }) { Text("OK") }
    }
}

Ein Nutzer kann diese Schaltfläche wieder fokussierbar machen, indem er canFocus auf true setzt:

FancyButton(Modifier.focusProperties { canFocus = true })

Wie jedes Modifier verhalten sich auch fokussierte Daten je nach Reihenfolge unterschiedlich. die Sie deklariert haben. Mit folgendem Code wird beispielsweise die Box fokussierbar, aber FocusRequester ist diesem fokussierbaren Element nicht zugeordnet, nach dem fokussierbaren Element deklariert.

Box(
    Modifier
        .focusable()
        .focusRequester(Default)
        .onFocusChanged {}
)

Ein focusRequester ist mit der ersten in der Hierarchie darunter fokussierbar, sodass focusRequester auf das ersten fokussierbaren Kind. Falls keiner verfügbar ist, verweist er auf nichts. Da Box jedoch (dank des focusable()-Modifikator) fokussierbar ist, können Sie sie in zwei Richtungen aufrufen.

Als weiteres Beispiel würde eine der folgenden Aktionen funktionieren, da onFocusChanged() ist das erste fokussierbare Element, das nach dem focusable()- oder focusTarget()-Modifikatoren.

Box(
    Modifier
        .onFocusChanged {}
        .focusRequester(Default)
        .focusable()
)
Box(
    Modifier
        .focusRequester(Default)
        .onFocusChanged {}
        .focusable()
)

Fokus beim Betreten oder Verlassen der Seite umleiten

Manchmal müssen Sie eine sehr spezifische Art der Navigation bereitstellen, wie z. B. die wie in der Animation unten dargestellt:

<ph type="x-smartling-placeholder">
</ph> Animation eines Bildschirms, auf dem zwei Spalten mit Schaltflächen nebeneinander angeordnet sind und der Fokus von einer Spalte zur anderen animiert wird.
Abbildung 3: Animation eines Bildschirms, auf dem zwei Spalten mit Schaltflächen nebeneinander angeordnet sind und der Fokus von einer Spalte zur anderen animiert wird.

Bevor wir näher darauf eingehen, sollten wir die Standardeinstellungen der Fokussuche. Sobald der Fokus auf die Suche erreicht das Element Clickable 3, indem ich DOWN auf dem Steuerkreuz (oder ein gleichwertiges Steuerelement) drückt wird der Fokus auf den Bereich unter Column verschoben, die Gruppe verlassen und die rechte Gruppe ignorieren. Wenn keine fokussierbaren Elementen verfügbar ist, wird der Fokus nicht verschoben, sondern auf Clickable 3

Um dieses Verhalten zu ändern und die gewünschte Navigation zu ermöglichen, können Sie das focusProperties-Modifikator, mit dem Sie festlegen können, was passiert, wenn der Fokus die Suche in den Composable ein- oder beendet:

val otherComposable = remember { FocusRequester() }

Modifier.focusProperties {
    exit = { focusDirection ->
        when (focusDirection) {
            Right -> Cancel
            Down -> otherComposable
            else -> Default
        }
    }
}

Es ist möglich, den Fokus auf eine bestimmte Composable zu richten, sobald diese in den oder verlässt einen bestimmten Teil der Hierarchie, z. B. wenn Ihre Benutzeroberfläche zwei Elemente und Sie möchten sicherstellen, dass bei der ersten Verarbeitung Fokus wechselt zur Sekunde:

<ph type="x-smartling-placeholder">
</ph> Animation eines Bildschirms, auf dem zwei Spalten mit Schaltflächen nebeneinander angeordnet sind und der Fokus von einer Spalte zur anderen animiert wird.
Abbildung 4: Animation eines Bildschirms, auf dem zwei Spalten mit Schaltflächen nebeneinander angeordnet sind und der Fokus von einer Spalte zur anderen animiert wird.

Wenn in diesem GIF der Fokus Clickable 3 Composable in Column 1 erreicht, Das nächste fokussierte Element ist Clickable 4 in einem anderen Column. Dieses Verhalten kann erreicht werden, indem focusDirection mit enter und exit kombiniert wird. -Werte innerhalb des focusProperties-Modifikator enthalten. Beide brauchen ein Lambda, als Parameter die Richtung, aus der der Fokus kommt, und gibt ein FocusRequester Dieses Lambda kann sich auf drei verschiedene Arten verhalten: FocusRequester.Cancel verhindert, dass der Fokus fortgesetzt wird, während FocusRequester.Default ändert sein Verhalten nicht. Wenn Sie stattdessen die Wenn FocusRequester an ein weiteres Composable-Element angehängt ist, springt der Fokus darauf bestimmte Composable.

Fortsetzungsrichtung des Fokus ändern

Um den Fokus auf das nächste Element oder eine bestimmte Richtung zu verschieben, können Sie den onPreviewKey-Modifikator zu nutzen und implizieren, dass LocalFocusManager stellen Sie den Fokus mit dem moveFocus-Modifikator vor.

Das folgende Beispiel zeigt das Standardverhalten des Fokusmechanismus: Wenn ein tab-Tastendruck erkannt, der Fokus rückt zum nächsten Element im Fokus Liste. Normalerweise müssen Sie dies nicht konfigurieren, um die Funktionsweise des Systems zu kennen, um die verhalten.

val focusManager = LocalFocusManager.current
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.onPreviewKeyEvent {
        when {
            KeyEventType.KeyUp == it.type && Key.Tab == it.key -> {
                focusManager.moveFocus(FocusDirection.Next)
                true
            }

            else -> false
        }
    }
)

In diesem Beispiel verlagert die Funktion focusManager.moveFocus() den Fokus auf zum angegebenen Element oder in die im Funktionsparameter angegebene Richtung.