Manchmal muss das Standardfokusverhalten der Elemente auf Ihrem Bildschirm überschrieben werden. So können Sie beispielsweise zusammensetzbare Funktionen gruppieren, den Fokus auf eine bestimmte zusammensetzbare Funktion verhindern, den Fokus explizit auf eine zusammensetzbare Funktion anfordern, den Fokus erfassen oder freigeben oder den Fokus weiterleiten beim Ein- oder Beenden. In diesem Abschnitt wird beschrieben, wie Sie das Fokusverhalten ändern können, wenn die Standardeinstellungen nicht Ihren Anforderungen entsprechen.
Sorgen Sie für eine kohärente Navigation mit Fokusgruppen
Manchmal errät Jetpack Compose das richtige nächste Element für die Navigation mit Tabs nicht sofort, insbesondere wenn komplexe übergeordnete Composables
wie Tabs und Listen ins Spiel kommen.
Die fokussierte Suche folgt normalerweise der Deklarationsreihenfolge von Composables
. Dies ist jedoch in manchen Fällen unmöglich, z. B. wenn eine der Composables
in der Hierarchie horizontal scrollbar ist und nicht vollständig sichtbar ist. Das wird im Beispiel unten gezeigt.
Jetpack Compose kann den Fokus auf das nächste Element richten, das am Anfang des Bildschirms liegt, wie unten gezeigt, anstatt den Pfad fortzusetzen, den Sie für die unidirektionale Navigation erwarten:
In diesem Beispiel ist klar, dass die Entwickler nicht beabsichtigt hatten, vom Tab Schokolade zum ersten Bild unten und dann wieder zum Tab Süßwaren zu springen. Der Fokus sollte stattdessen auf den Tabs bis zum letzten Tab und dann auf den inneren Inhalt liegen:
In Situationen, in denen es wichtig ist, dass eine Gruppe von zusammensetzbaren Funktionen nacheinander hervorgehoben wird, wie in der Tabzeile aus dem vorherigen Beispiel, müssen Sie die Composable
in ein übergeordnetes Element umschließen, das den focusGroup()
-Modifikator enthält:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
Bei der bidirektionalen Navigation wird nach der nächstgelegenen zusammensetzbaren Funktion für die angegebene Richtung gesucht. Wenn ein Element aus einer anderen Gruppe näher liegt als ein nicht vollständig sichtbares Element in der aktuellen Gruppe, wird das nächstgelegene Element ausgewählt. Um dies zu vermeiden, können Sie den focusGroup()
-Modifikator anwenden.
FocusGroup
lässt eine ganze Gruppe im Hinblick auf den Fokus wie eine einzelne Einheit erscheinen. Die Gruppe selbst wird jedoch nicht im Fokus. Stattdessen wird das nächstgelegene untergeordnete Element in den Fokus rücken. Auf diese Weise weiß die Navigation, das nicht vollständig sichtbare Element aufzurufen, bevor die Gruppe verlassen wird.
In diesem Fall werden die drei Instanzen von FilterChip
vor den SweetsCard
-Elementen hervorgehoben, auch wenn die SweetsCards
für den Nutzer vollständig sichtbar sind und einige FilterChip
möglicherweise ausgeblendet sind. Das liegt daran, dass der focusGroup
-Modifikator den Fokusmanager anweist, die Reihenfolge anzupassen, in der die Elemente fokussiert werden, um die Navigation einfacher und kohärenter zu gestalten.
Wenn FilterChipC
ohne den Modifikator focusGroup
nicht sichtbar wäre, wird sie von der Fokusnavigation zuletzt aufgerufen. Wenn Sie einen solchen Modifikator hinzufügen, wird er jedoch nicht nur sichtbar, sondern wird auch direkt nach FilterChipB
hervorgehoben, wie es Nutzer erwarten würden.
Eine zusammensetzbare fokussierbare Funktion erstellen
Einige zusammensetzbare Funktionen sind fokussierbar, z. B. eine Schaltfläche oder eine zusammensetzbare Funktion, an die der clickable
-Modifikator angehängt ist. Wenn Sie einer zusammensetzbaren Funktion ausdrücklich ein fokussierbares Verhalten hinzufügen möchten, verwenden Sie den Modifikator focusable
:
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 nicht fokussierbar machen
Es kann Situationen geben, in denen einige Ihrer Elemente nicht im Fokus stehen sollten. In diesen seltenen Fällen kannst du mit canFocus property
verhindern, dass ein Composable
fokussierbar ist.
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 kann es erforderlich sein, den Fokus als Antwort auf eine Nutzerinteraktion explizit anzufordern. Sie können beispielsweise einen Nutzer fragen, ob er ein Formular noch einmal ausfüllen möchte. Wenn er auf „Ja“ klickt, wird das erste Feld des Formulars neu fokussiert.
Verknüpfen Sie zuerst ein FocusRequester
-Objekt mit der zusammensetzbaren Funktion, auf die Sie den Tastaturfokus verschieben möchten. Im folgenden Code-Snippet wird ein FocusRequester
-Objekt mit einem TextField
verknüpft, indem ein Modifizierer namens Modifier.focusRequester
festgelegt wird:
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 sie bei jeder Neuzusammensetzung noch einmal ausgeführt. Das folgende Snippet zeigt, wie Sie das System auffordern, den Tastaturfokus zu verschieben, wenn auf die Schaltfläche geklickt wird:
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 erfassen und loslassen
Sie können Fokus nutzen, um Ihre Nutzer zu motivieren, die richtigen Daten bereitzustellen, die Ihre App für ihre Aufgabe benötigt, z. B. eine gültige E-Mail-Adresse oder Telefonnummer. Obwohl Fehlerstatus Ihre Nutzer über das Problem informieren, benötigen Sie möglicherweise das Feld mit den fehlerhaften Informationen, um konzentriert zu bleiben, bis es behoben ist.
Um den Fokus zu erfassen, können Sie die Methode captureFocus()
aufrufen und anschließend wie im folgenden Beispiel mit der Methode freeFocus()
freigeben:
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
können als Elemente angesehen werden, die nur ein untergeordnetes Element haben. Wenn Sie sie in die Warteschlange stellen, umschließt jedes Modifier
links (oder oben) das Modifier
, das rechts (oder darunter) folgt. Das bedeutet, dass die zweite Modifier
im ersten enthalten ist. Wenn also zwei focusProperties
deklariert werden, funktioniert nur die oberste, da die folgenden in der obersten enthalten sind.
Im folgenden Code wird das Konzept näher erläutert:
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
In diesem Fall wird die focusProperties
, die item2
als rechten Fokus angibt, nicht verwendet, da sie im vorherigen enthalten ist. Daher wird item1
verwendet.
Mit diesem Ansatz kann ein übergeordnetes Element das Verhalten auch mithilfe von FocusRequester.Default
auf die Standardeinstellungen zurücksetzen:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
Das übergeordnete Element muss nicht Teil derselben Modifikatorkette sein. Eine übergeordnete zusammensetzbare Funktion kann die Fokuseigenschaft einer untergeordneten zusammensetzbaren Funktion überschreiben. Beispielsweise ist die Schaltfläche durch den FancyButton
nicht fokussierbar:
@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 alle Modifier
verhalten sich auch fokussierte Elemente je nach ihrer deklarierten Reihenfolge unterschiedlich. Beispielsweise macht Code wie der folgende das Box
fokussierbar, aber FocusRequester
ist diesem fokussierbaren Element nicht zugeordnet, da es nach dem fokussierbaren Element deklariert wird.
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
Ein focusRequester
ist mit dem ersten fokussierbaren Element darunter verknüpft, das in der Hierarchie darunter liegt. Daher verweist dieses focusRequester
auf das erste fokussierbare untergeordnete Element. Falls keiner vorhanden ist, verweist er auf nichts.
Da die Box
jedoch fokussierbar ist (dank des focusable()
-Modifikators), können Sie sie über die bidirektionale Navigation aufrufen.
Als weiteres Beispiel würde eine der folgenden Methoden funktionieren, da sich der Modifizierer onFocusChanged()
auf das erste fokussierbare Element bezieht, das nach dem focusable()
- oder focusTarget()
-Modifikator angezeigt wird.
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
Fokus beim Betreten oder Verlassen weiterleiten
Manchmal müssen Sie eine ganz bestimmte Art von Navigation bereitstellen, wie in der folgenden Animation gezeigt:
Bevor wir damit beginnen, wie dies erstellt wird, müssen Sie das Standardverhalten der fokussierten Suche verstehen. Sobald die fokussierte Suche das Element Clickable 3
erreicht, wird durch Drücken des Steuerkreuzes DOWN
(oder der entsprechenden Pfeiltaste) der Fokus auf den Bereich unterhalb von Column
verschoben. Dadurch wird die Gruppe verlassen und die Markierung rechts wird ignoriert. Wenn keine fokussierbaren Elemente verfügbar sind, wird der Fokus nirgendwo verschoben, sondern bleibt bei Clickable 3
.
Wenn Sie dieses Verhalten ändern und die gewünschte Navigation bereitstellen möchten, können Sie den focusProperties
-Modifikator verwenden. Damit können Sie festlegen, was passiert, wenn die Fokussuche in Composable
ein- oder ausläuft:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
Es ist möglich, den Fokus immer dann auf eine bestimmte Composable
zu richten, wenn diese einen bestimmten Teil der Hierarchie betritt oder verlässt. Das ist beispielsweise der Fall, wenn Ihre UI zwei Spalten hat und Sie sicherstellen möchten, dass bei der Verarbeitung der ersten Spalte zum zweiten gewechselt wird:
Sobald der Fokus in diesem GIF die Clickable 3 Composable
in Column
1 erreicht, ist das nächste Element, das hervorgehoben wird, Clickable 4
in einem anderen Column
. Um dieses Verhalten zu erreichen, können Sie focusDirection
mit den Werten enter
und exit
im focusProperties
-Modifikator kombinieren. Beide benötigen ein Lambda, das als Parameter die Richtung verwendet, aus der der Fokus kommt, und ein FocusRequester
zurückgibt. Dieses Lambda kann sich auf drei verschiedene Arten verhalten: Durch die Rückgabe von FocusRequester.Cancel
wird der Fokus nicht mehr fortgesetzt, während FocusRequester.Default
sein Verhalten nicht ändert. Wenn stattdessen das FocusRequester
an ein anderes Composable
angehängt ist, kann der Fokus zu diesem spezifischen Composable
springen.
Richtung des Fokus ändern
Um den Fokus auf das nächste Element oder in eine genaue Richtung zu verschieben, können Sie den onPreviewKey
-Modifikator nutzen und den LocalFocusManager
mit dem Modifikator moveFocus
andeuten, um den Fokus zu verstärken.
Das folgende Beispiel zeigt das Standardverhalten des Fokusmechanismus: Wenn ein tab
-Tastendruck erkannt wird, wird zum nächsten Element in der Fokusliste gewechselt. Auch wenn dies normalerweise nicht konfiguriert werden muss, ist es wichtig, das Innenleben des Systems zu kennen, um das Standardverhalten ändern zu können.
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 bewegt die focusManager.moveFocus()
-Funktion den Fokus auf das angegebene Element oder die im Funktionsparameter angegebene Richtung.
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Mehr Konzentration
- Auf „Schreiben“ fokussieren
- Reihenfolge des Durchlaufs für den Fokus ändern