À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:
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:
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 = remember { 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:
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:
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