Em dispositivos de tela grande, os usuários costumam interagir com apps usando um teclado, mouse, trackpad, stylus ou gamepad. Para permitir que o app aceite entrada de dispositivos externos, é preciso fazer o seguinte:
- Teste o suporte básico ao teclado, como Ctrl+Z para desfazer, Ctrl+C para copiar e Ctrl+S para salvar. Consulte Processar ações do teclado para ver uma lista de atalhos de teclado padrão.
- Teste o suporte avançado do teclado, por exemplo, a navegação usando as teclas Tab e de setas, Enter para confirmar a entrada de texto e a barra de espaço para reproduzir e pausar em apps de música.
- Teste as interações básicas do mouse, incluindo o clique com o botão direito do mouse para abrir o menu de contexto, as mudanças de ícone ao passar o cursor e os eventos de rolagem da roda do mouse ou do trackpad em componentes personalizados.
- Teste dispositivos de entrada específicos do app, como uma stylus, controles de jogos e controles MIDI de apps de música.
- Considere o suporte avançado de entrada que pode destacar o app em ambientes de computador. Por exemplo, poder usar o touchpad como um modificador para apps de DJ, a captura de mouse para jogos e mais atalhos de teclado para usuários focados nesse dispositivo.
Teclado
A forma como o app responde à entrada do teclado contribui para a experiência do usuário em telas grandes. Existem três tipos de entrada de teclado: navegação, pressionamentos de tecla e atalhos.
Navegação
A navegação pelo teclado raramente é implementada em apps centrados em toque, mas os usuários esperam que ela esteja disponível quando usam um app e estão com as mãos no teclado. A navegação por teclado pode ser essencial em smartphones, tablets, dispositivos dobráveis e dispositivos desktop para usuários com necessidades de acessibilidade.
Para muitos apps, a navegação com as teclas de seta e Tab é processada
automaticamente pelo framework do Android. Por exemplo, alguns elementos que podem ser compostos são
focalizáveis por padrão, como um Button ou um elemento que pode ser composto com o
modificador clickable. A navegação pelo teclado geralmente funciona sem nenhum
código extra. Para ativar a navegação pelo teclado em elementos combináveis personalizados que não
são focalizáveis por padrão, adicione 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") }
Para mais informações, consulte Como tornar um elemento combinável focalizável.
Quando o foco está ativado, o framework do Android cria um mapeamento de navegação para todos os componentes focalizáveis com base na posição deles. Isso geralmente funciona como esperado, e nenhum desenvolvimento adicional é necessário.
No entanto, o Compose nem sempre determina o próximo item correto para a navegação por guias em elementos combináveis complexos, como guias e listas, por exemplo, quando um dos elementos combináveis é um elemento rolável horizontal que não está totalmente visível.
Para controlar o comportamento do foco, adicione o modificador focusGroup ao elemento combinável pai de uma coleção de elementos combináveis. O foco se move para o grupo e, em seguida,
pelo grupo antes de passar para o próximo componente focalizável, por exemplo:
Row {
Column(Modifier.focusGroup()) {
Button({}) { Text("Row1 Col1") }
Button({}) { Text("Row2 Col1") }
Button({}) { Text("Row3 Col1") }
}
Column(Modifier.focusGroup()) {
Button({}) { Text("Row1 Col2") }
Button({}) { Text("Row2 Col2") }
Button({}) { Text("Row3 Col2") }
}
}
Para mais informações, consulte Oferecer uma navegação coerente com grupos de discussão.
Teste o acesso a todos os elementos da interface do app usando apenas o teclado. Os elementos usados com frequência precisam ser acessíveis sem o uso do mouse ou da entrada por toque.
A compatibilidade com o teclado pode ser essencial para usuários com necessidades de acessibilidade.
Pressionamentos de tecla
Para entrada de texto que seria processada por um teclado virtual na tela (IME),
como para
um TextField
, os apps precisam se comportar como esperado em dispositivos de tela grande sem trabalho de desenvolvimento
adicional. Para as teclas que não podem ser previstas pelo framework,
os apps precisam processar o comportamento por conta própria. Isso ocorre especialmente em apps
com visualizações personalizadas.
Alguns exemplos são apps de chat que usam a tecla Enter para enviar uma mensagem, apps de mídia que iniciam e interrompem a reprodução com a barra de espaço e jogos que controlam o movimento com as teclas w, a, s e d.
É possível processar pressionamentos de tecla individuais com o modificador onKeyEvent, que
aceita uma lambda chamada quando o componente modificado recebe um evento de tecla.
A propriedade KeyEvent#type permite determinar se o evento é um
pressionamento de tecla (KeyDown) ou uma liberação de tecla (KeyUp):
Box(
modifier = Modifier.focusable().onKeyEvent {
if(
it.type == KeyEventType.KeyUp &&
it.key == Key.S
) {
doSomething()
true
} else {
false
}
}
) {
Text("Press S key")
}
Como alternativa, é possível substituir o callback onKeyUp() e adicionar o
comportamento esperado para cada código de tecla recebido:
kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
return when (keyCode) {
KeyEvent.KEYCODE_ENTER -> {
sendChatMessage()
true
}
KeyEvent.KEYCODE_SPACE -> {
playOrPauseMedia()
true
}
else -> super.onKeyUp(keyCode, event)
}
}
Um evento onKeyUp ocorre quando uma tecla é solta. O uso do callback
evita que os apps precisem processar vários eventos onKeyDown se uma tecla
for pressionada ou solta lentamente. Jogos e apps que precisam detectar o momento em que uma
tecla é pressionada ou se o usuário está mantendo uma tecla pressionada podem detectar o
evento onKeyDown e processar eventos onKeyDown repetidos.
Para mais informações, consulte Processar ações do teclado.
Atalhos
Atalhos de teclado comuns que incluem as teclas Ctrl, Alt, Shift e Meta são esperados ao usar um teclado físico. Se um app não implementar atalhos, a experiência pode ser frustrante para os usuários. Os usuários avançados também gostam de atalhos para tarefas frequentes específicas do app. Os atalhos facilitam o uso de um app e o diferenciam de apps que não têm atalhos.
Alguns atalhos comuns incluem Ctrl+S (salvar), Ctrl+Z (desfazer) e Ctrl+Shift+Z (refazer). Para ver uma lista de atalhos padrão, consulte Processar ações do teclado.
Um objeto KeyEvent tem os seguintes atributos que indicam se
as teclas modificadoras estão pressionadas:
Exemplo:
Box(
Modifier.onKeyEvent {
if (it.isAltPressed && it.key == Key.A) {
println("Alt + A is pressed")
true
} else {
false
}
}
.focusable()
)
Para ver mais informações, consulte os seguintes tópicos:
Stylus
Muitos dispositivos de tela grande vêm com uma stylus. Os apps Android processam as stylus como entrada de touch screen. Alguns dispositivos também podem ter uma mesa digitalizadora USB ou Bluetooth, como a Wacom Intuos. Os apps Android podem receber entradas por Bluetooth, mas não por USB.
Para acessar objetos MotionEvent da stylus, adicione o modificador pointerInteropFilter
a uma superfície de desenho. Implemente uma classe ViewModel com um método que
processe eventos de movimento. Transmita o método como a lambda onTouchEvent do
modificador pointerInteropFilter:
@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
Canvas(modifier = modifier
.clipToBounds()
.pointerInteropFilter {
viewModel.processMotionEvent(it)
}
) {
// Drawing code here.
}
}
O objeto MotionEvent contém informações sobre o evento:
MotionEvent#getToolType()retornaTOOL_TYPE_FINGER,TOOL_TYPE_STYLUSouTOOL_TYPE_ERASER, dependendo da ferramenta que entrou em contato com a tela.MotionEvent#getPressure()informa a pressão física aplicada à caneta stylus (se houver suporte)MotionEvent#getAxisValue()comMotionEvent.AXIS_TILTeMotionEvent.AXIS_ORIENTATIONfornecem a inclinação física e a orientação da stylus (se houver suporte)
Pontos históricos
O Android agrupa eventos de entrada e os envia uma vez por frame. Uma stylus pode
informar eventos em frequências muito mais altas do que a tela. Ao criar apps de desenho, verifique se há eventos que podem ter ocorrido recentemente usando as
APIs getHistorical:
MotionEvent#getHistoricalX()MotionEvent#getHistoricalY()MotionEvent#getHistoricalPressure()MotionEvent#getHistoricalAxisValue()
Rejeição da palma da mão
Quando os usuários desenham, escrevem ou interagem com o app usando uma stylus, às vezes,
eles tocam na tela com a palma da mão. O evento de toque (definido como
ACTION_DOWN ou ACTION_POINTER_DOWN) pode ser informado ao app
antes que o sistema reconheça e desconsidere o toque acidental da palma da mão.
No Android, eventos de toque da palma da mão são cancelados pelo envio de um MotionEvent. Se o
app receber ACTION_CANCEL, cancele o gesto. Se o app receber
ACTION_POINTER_UP, verifique se FLAG_CANCELED está definido. Nesse caso, cancele
o gesto.
Não verifique apenas o FLAG_CANCELED. No Android 13 (nível 33 da API) e versões mais recentes,
o sistema define FLAG_CANCELED para eventos ACTION_CANCEL, mas não
define a flag em versões anteriores do Android.
Android 12
No Android 12 (nível 32 da API) e versões anteriores, a detecção da rejeição da palma da mão é possível
apenas para eventos de toque de ponteiro único. Se um toque da palma for o único ponteiro, o
sistema vai cancelar o evento definindo ACTION_CANCEL no objeto do evento de movimento. Se outros ponteiros estiverem inativos, o sistema vai definir ACTION_POINTER_UP, o que é
suficiente para detectar a rejeição da palma da mão.
Android 13
No Android 13 (API de nível 33) e versões mais recentes, se um toque de palma da mão for o único ponteiro,
o sistema vai cancelar o evento definindo ACTION_CANCEL e FLAG_CANCELED no
objeto do evento de movimento. Se outros ponteiros estiverem inativos, o sistema vai definir
ACTION_POINTER_UP e FLAG_CANCELED.
Sempre que o app receber um evento de movimento com ACTION_POINTER_UP, procure o
FLAG_CANCELED para determinar se ele indica uma rejeição de palma da mão (ou
outro cancelamento de eventos).
Apps de anotações
O ChromeOS tem uma intent especial que mostra apps de anotações registrados para os usuários. Para registrar um app como de anotações, adicione o seguinte ao manifesto do app:
<intent-filter>
<action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Quando um app é registrado no sistema, o usuário pode selecioná-lo como o app de anotações
padrão. Quando uma nova nota é solicitada, o app precisa criar uma
nota vazia pronta para entrada de stylus. Quando o usuário quer fazer anotações em uma imagem
(como uma captura de tela ou uma imagem baixada), o app é iniciado com ClipData,
que contém um ou mais itens com URIs content://. O app precisa criar uma
nota que use a primeira imagem anexada como plano de fundo e entrar em um modo
em que o usuário possa desenhar na tela com uma stylus.
Testar intents de anotações sem uma stylus
Para testar se um app responde corretamente a intents de anotação sem uma stylus ativa, use o seguinte método para mostrar as opções de anotação no ChromeOS:
- Alterne para o modo de desenvolvedor e torne o dispositivo gravável.
- Pressione Ctrl+Alt+F2 para abrir um terminal.
- Execute o comando
sudo vi /etc/chrome_dev.conf. - Pressione
ipara editar e adicione--ash-enable-palettea uma nova linha no final do arquivo. - Salve pressionando Esc e digitando :, w, q e pressionando Enter.
- Pressione Ctrl+Alt+F1 para retornar à interface normal do ChromeOS.
- Sair e fazer login novamente
Um menu da stylus vai aparecer na estante:
- Toque no botão da stylus na estante e escolha Nova nota. Isso vai abrir uma nota de desenho em branco.
- Faça uma captura de tela. Na estante, selecione botão da stylus > Capturar tela ou faça o download de uma imagem. A opção Anotar imagem deve estar disponível na notificação. O app vai iniciar com a imagem pronta para ser anotada.
Compatibilidade com mouse e touchpad
A maioria dos apps geralmente precisa processar apenas três eventos centrados em telas grandes: clique com o botão direito do mouse, passar o cursor e arrastar e soltar.
Clicar com o botão direito
Todas as ações que fazem com que um app mostre um menu de contexto, por exemplo, tocar em um item de lista e pressionar, também precisam reagir a eventos de clique com o botão direito do mouse.
Para processar eventos de clique com o botão direito do mouse, os apps precisam registrar um
View.OnContextClickListener:
Box(modifier = Modifier.fillMaxSize()) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
val rootView = FrameLayout(context)
val onContextClickListener =
View.OnContextClickListener { view ->
showContextMenu()
true
}
rootView.setOnContextClickListener(onContextClickListener)
rootView
},
)
}
Para saber como criar menus de contexto, consulte Criar um menu contextual.
Passar cursor
Para deixar os layouts do app mais refinados e fáceis de usar, processe eventos de passagem do cursor. Isso é especialmente verdadeiro para componentes personalizados do :
Visualizações deOs dois exemplos mais comuns são:
- Indicar aos usuários se um elemento tem comportamento interativo, como ser clicável ou editável, mudando o ícone do ponteiro do mouse.
- adicionar feedback visual a itens em uma grade ou lista grande quando o ponteiro do mouse estiver sobre eles.
Arrastar e soltar
Em um ambiente de várias janelas, os usuários esperam poder arrastar e soltar itens entre apps. Isso vale para dispositivos desktop, tablets, smartphones e dobráveis no modo de tela dividida.
Considere se os usuários vão arrastar itens para o app. Por exemplo, editores de fotos provavelmente recebem fotos, players de áudio recebem arquivos de áudio e programas de desenho recebem imagens.
Para adicionar suporte ao recurso de arrastar e soltar, consulte Arrastar e soltar e confira a postagem do blog Android no ChromeOS: como implementar o recurso de arrastar e soltar (em inglês).
Considerações especiais para o ChromeOS
- Não se esqueça de pedir permissão usando
requestDragAndDropPermissions()para acessar itens arrastados de fora do app. Um item precisa ter a flag
View.DRAG_FLAG_GLOBALpara ser arrastado para outros aplicativos.Consulte Iniciar um evento de arrastar
Compatibilidade avançada com ponteiro
Os apps que fazem o processamento avançado de entrada de mouse e touchpad precisam implementar um modificador
pointerInput para receber um PointerEvent:
@Composable private fun LogPointerEvents(filter: PointerEventType? = null) { var log by remember { mutableStateOf("") } Column { Text(log) Box( Modifier .size(100.dp) .background(Color.Red) .pointerInput(filter) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() // handle pointer event if (filter == null || event.type == filter) { log = "${event.type}, ${event.changes.first().position}" } } } } ) } }
Examine o objeto PointerEvent para determinar o seguinte:
PointerType: mouse, stylus, toque etc. dePointerEvent#changesPointerEventType: ações do ponteiro, como pressionar, mover, rolar e soltar.
Controles de jogos
Alguns dispositivos Android de tela grande oferecem suporte para até quatro controles de jogo. Use as APIs padrão de controle de jogo do Android para processar controles de jogo (consulte Suporte a controles de jogo).
Os botões do controle de jogo são mapeados para valores comuns após um mapeamento comum. Mas nem todos os fabricantes de controles de jogos seguem as mesmas convenções de mapeamento. Você pode fornecer uma experiência muito melhor aos usuários se permitir que eles selecionem entre os mapeamentos de controles mais usados. Consulte Processar pressionamentos de botões do gamepad para mais informações.
Modo de tradução de entrada
O ChromeOS ativa um modo de tradução de entrada por padrão. Para a maioria dos apps Android, esse modo os ajuda a funcionar como esperado em um ambiente de computador. Alguns exemplos incluem a ativação automática da rolagem com dois dedos no touchpad, da rolagem com a roda do mouse e do mapeamento de coordenadas de exibição brutas para coordenadas de janela. Em geral, os desenvolvedores de apps não precisam implementar nenhum desses comportamentos.
Se um app implementar um comportamento de entrada personalizado, como, por exemplo, definir uma ação personalizada de gesto de pinça com dois dedos, ou se essas traduções de entrada não fornecerem os eventos de entrada esperados pelo app, você pode desativar o modo de tradução de entrada adicionando esta tag ao manifesto do Android:
<uses-feature
android:name="android.hardware.type.pc"
android:required="false" />