Às vezes, é necessário substituir o comportamento de foco padrão dos elementos na tela. Por exemplo, talvez você queira agrupar elementos combináveis, impedir se concentrar em um determinado elemento combinável, solicitar o foco explicitamente em um deles; capturar ou soltar o foco ou redirecionar o foco na entrada ou saída. Isso descreve como alterar o comportamento de foco quando os padrões não são os mesmos que você precisam.
Ofereça uma navegação coerente com grupos focais
Às vezes, o Jetpack Compose não adivinha imediatamente o próximo item correto para
navegação com guias, especialmente quando Composables
principais complexas, como guias e
listas entram em jogo.
Embora a pesquisa de foco geralmente siga a ordem de declaração do Composables
,
Isso é impossível em alguns casos, como quando um dos Composables
na
é uma hierarquia horizontal rolável que não é totalmente visível. Isso é mostrado
no exemplo abaixo.
O Jetpack Compose pode decidir focar no próximo item mais próximo do início do como mostrado abaixo, em vez de continuar no caminho esperado navegação unidirecional:
![Animação de um app mostrando uma navegação horizontal superior e uma lista de itens abaixo.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_1.gif?authuser=5&hl=pt)
Neste exemplo, fica claro que os desenvolvedores não pretendiam que o foco pular da guia Chocolates para a primeira imagem abaixo e voltar para a Guia Pastéis. Em vez disso, eles queriam que o foco continuasse nas guias até que última guia e, em seguida, concentre-se no conteúdo interno:
![Animação de um app mostrando uma navegação horizontal superior e uma lista de itens abaixo.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_2.gif?authuser=5&hl=pt)
Em situações em que é importante que um grupo de elementos combináveis tenha foco
sequencialmente, como na linha Tab do exemplo anterior, é necessário unir
o Composable
em um pai que tem o modificador focusGroup()
:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
A navegação bidirecional procura o elemento combinável mais próximo para o objeto
direção, se um elemento de outro grupo estiver mais próximo do que um elemento não totalmente visível
item no grupo atual, a navegação escolhe o mais próximo. Para evitar isso,
comportamento, aplique o modificador focusGroup()
.
FocusGroup
faz um grupo inteiro parecer uma única entidade em termos de foco.
mas o grupo em si não terá o foco. Em vez disso, a criança mais próxima
ganhar foco. Dessa forma, a navegação sabe que deve ir até o destino
item antes de sair do grupo.
Nesse caso, as três instâncias de FilterChip
serão focadas antes do
SweetsCard
itens, mesmo quando SweetsCards
estão completamente visíveis para o
usuário e alguns FilterChip
podem estar ocultos. Isso acontece porque
O modificador focusGroup
instrui o gerenciador de foco a ajustar a ordem em que os itens
estão focados para que a navegação seja mais fácil e coerente com a interface.
Sem o modificador focusGroup
, se o FilterChipC
não estiver visível, coloque o foco
a navegação a pegava por último. No entanto, adicionar esse modificador faz com que não
pode ser descoberto, mas também terá foco logo após FilterChipB
, conforme
que os usuários esperam.
Como tornar um elemento combinável focalizável
Alguns elementos combináveis são focalizáveis por design, como um botão ou um elemento combinável com
o modificador clickable
anexado a ela. Se você quiser adicionar especificamente
comportamento focalizável para um elemento combinável, use o modificador focusable
:
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
Como tornar um elemento combinável não focalizável
Pode haver situações em que alguns dos seus elementos não devem participar
no foco. Nessas raras ocasiões, você pode usar o canFocus property
para impedir que uma Composable
seja focalizável.
var checked by remember { mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, // Prevent component from being focused modifier = Modifier .focusProperties { canFocus = false } )
Solicitar foco do teclado com FocusRequester
Em alguns casos, você pode querer solicitar o foco explicitamente como uma resposta interação do usuário. Por exemplo, é possível perguntar a um usuário se ele quer reiniciar preencher um formulário e, se pressionarem "sim", mude o foco do primeiro campo dessa forma.
A primeira coisa a fazer é associar um objeto FocusRequester
ao
combinável para onde você quer mover o foco do teclado. No código a seguir,
snippet, um objeto FocusRequester
é associado a um TextField
definindo um
chamado Modifier.focusRequester
:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
Você pode chamar o método requestFocus
do FocusRequester para enviar solicitações de foco reais. Invoque esse método fora de um contexto Composable
Caso contrário, ele é executado novamente a cada recomposição. O snippet a seguir
mostra como solicitar que o sistema mova o foco do teclado quando o botão for
clicou em:
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") }
Capturar e liberar o foco
Aproveite o foco para orientar seus usuários e fornecer os dados corretos ao seu app. para realizar a tarefa, por exemplo, conseguir um endereço de e-mail ou telefone válido número Embora os estados de erro informem os usuários sobre o que está acontecendo, você pode precisar que o campo com informações incorretas permaneça focado até que seja corrigidos.
Para capturar o foco, você pode invocar o método captureFocus()
e
Solte-o depois com o método freeFocus()
, como no seguinte
exemplo:
val textField = FocusRequester() TextField( value = text, onValueChange = { text = it if (it.length > 3) { textField.captureFocus() } else { textField.freeFocus() } }, modifier = Modifier.focusRequester(textField) )
Precedência dos modificadores de foco
Modifiers
podem ser vistas como elementos que têm apenas um filho. Portanto, quando você enfileira
cada uma das Modifier
à esquerda (ou na parte de cima) envolve a Modifier
que segue
a direita (ou abaixo). Isso significa que o segundo Modifier
está dentro
a primeira, de modo que, ao declarar dois focusProperties
, apenas o
um funciona, porque os seguintes estão contidos na parte superior.
Para esclarecer mais o conceito, consulte o código a seguir:
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
Nesse caso, a focusProperties
que indica item2
como o foco correto
não será usada, conforme consta no anterior; portanto, item1
vai ser
um usado.
Com essa abordagem, o familiar responsável também pode redefinir o comportamento
usando FocusRequester.Default
:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
O elemento pai não precisa fazer parte da mesma cadeia de modificadores. Um familiar responsável
pode substituir uma propriedade de foco de um elemento filho. Por exemplo:
Considere este FancyButton
que torna o botão não focalizável:
@Composable fun FancyButton(modifier: Modifier = Modifier) { Row(modifier.focusProperties { canFocus = false }) { Text("Click me") Button(onClick = { }) { Text("OK") } } }
Um usuário pode tornar esse botão focalizável novamente definindo canFocus
como true
:
FancyButton(Modifier.focusProperties { canFocus = true })
Como todas as Modifier
, as relacionadas ao foco se comportam de maneira diferente com base na ordem
declará-las. Por exemplo, um código como o seguinte faz com que Box
focalizável, mas a FocusRequester
não está associada a ela, já que
é declarado após o focalizável.
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
É importante lembrar que um focusRequester
está associado ao primeiro
focalizável abaixo dele na hierarquia, de modo que esse focusRequester
aponte para o
primeiro filho focalizável. Caso nenhum esteja disponível, ele não apontará para nada.
No entanto, como o Box
é focalizável (graças ao modificador focusable()
),
você pode navegar até ele usando a navegação bidirecional.
Como outro exemplo, qualquer uma das opções a seguir funcionaria, já que o onFocusChanged()
refere-se ao primeiro elemento focalizável que aparece após o
Modificadores focusable()
ou focusTarget()
.
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
Redirecionar o foco na entrada ou saída
Às vezes, você precisa fornecer um tipo muito específico de navegação, como o como mostrado na animação abaixo:
![Animação de uma tela mostrando duas colunas de botões lado a lado e animando o foco de uma coluna para outra.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_1.gif?authuser=5&hl=pt)
Antes de nos aprofundarmos em como criar isso, é importante entender o padrão
da pesquisa de foco. Sem modificações, quando a pesquisa de foco
chega ao item Clickable 3
, pressionando DOWN
no botão direcional (ou equivalente
tecla de seta) moveria o foco para o que fosse exibido abaixo de Column
,
para sair do grupo e ignorar o grupo da direita. Se não houver
itens focalizáveis disponíveis, o foco não se move para nenhum lugar, mas permanece
Clickable 3
:
Para alterar esse comportamento e fornecer a navegação pretendida, você pode usar o
Modificador focusProperties
, que ajuda a gerenciar o que acontece quando o foco
a pesquisa entra ou sai do Composable
:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
É possível direcionar o foco para uma Composable
específica sempre que ela entra
ou sai de uma certa parte da hierarquia, por exemplo, quando a interface tem duas
colunas e quer ter certeza de que sempre que a primeira for processada,
muda para o segundo:
![Animação de uma tela mostrando duas colunas de botões lado a lado e animando o foco de uma coluna para outra.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_2.gif?authuser=5&hl=pt)
Neste GIF, assim que o foco atinge o Clickable 3 Composable
em Column
1,
o próximo item em foco é Clickable 4
em outro Column
. Esse comportamento
pode ser alcançada combinando focusDirection
com enter
e exit
valores dentro do modificador focusProperties
. Ambos precisam de uma lambda que leve
como parâmetro, a direção de onde vem o foco e retorna uma
FocusRequester
: Essa lambda pode se comportar de três maneiras diferentes: retornando
FocusRequester.Cancel
impede que o foco continue, enquanto
FocusRequester.Default
não muda o comportamento. Em vez disso, forneça o
A FocusRequester
anexada a outra Composable
faz o foco pular para ela
um Composable
específico.
Mudar a direção de avanço do foco
Para avançar o foco para o próximo item ou para uma direção precisa, você pode
use o modificador onPreviewKey
e implicar o LocalFocusManager
para
avançar o foco com o modificador moveFocus
.
O exemplo a seguir mostra o comportamento padrão do mecanismo de foco: quando um
Um pressionamento de tecla tab
é detectado, o foco avança para o próximo elemento em foco
lista. Embora isso não seja algo que você normalmente precise configurar, é importante
saber o funcionamento interno do sistema para poder alterar o padrão
do seu modelo.
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 } } )
Neste exemplo, a função focusManager.moveFocus()
avança o foco para
o item especificado ou a direção implícita no parâmetro da função.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Reaja ao foco
- Foco no Compose
- Mudar a ordem de travessia de foco