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 do teclado, como Ctrl+Z para desfazer, Ctrl+C para copiar e Ctrl+S para salvar. Consulte Processar ações do teclado para conferir uma lista de atalhos de teclado padrão.
- Testar o suporte avançado do teclado, por exemplo, a tecla Tab e a navegação pelo teclado com as teclas de seta, a tecla Enter para confirmar a entrada de texto e a barra de espaço para reproduzir e pausar em apps de mídia.
- Teste interações básicas do mouse, incluindo o botão direito do mouse para acessar o menu de contexto, mudanças de ícone ao passar o cursor e eventos de rolagem da roda do mouse ou trackpad em componentes personalizados.
- Testar dispositivos de entrada específicos do app, como stylus, controles de jogos e controladores 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 maneira como seu 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 voltados para o toque, mas os usuários esperam isso quando estão usando 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 computadores 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 combináveis são
focalizáveis por padrão, como um Button
ou um elemento combinável com o
modificador clickable
. A navegação pelo teclado geralmente funciona sem
nenhum código adicional. Para ativar a navegação pelo teclado para 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 focado.
Quando o foco é 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 conforme o esperado, e não é necessário realizar qualquer outro desenvolvimento.
No entanto, o Compose nem sempre determina o próximo item correto para a navegação com guias para elementos combináveis complexos, como guias e listas, por exemplo, quando um dos elementos combináveis é um rolável horizontal que não está totalmente visível.
Para controlar o comportamento de 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,
para o grupo antes de passar para o próximo componente em foco, 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 navegação coerente com grupos de foco.
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 mouse ou a entrada de 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
extra de desenvolvimento. Para pressionamentos de tecla que não podem ser previstos pelo framework,
os apps precisam processar o comportamento sozinhos. 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 é uma
pressão 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:
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 é liberada. O uso do callback
impede que os apps precisem processar vários eventos onKeyDown
se uma tecla
for mantida pressionada ou liberada lentamente. Jogos e apps que precisam detectar o momento em que uma
tecla é pressionada ou se o usuário está pressionando uma tecla podem detectar o
evento onKeyDown
e processar eventos onKeyDown
repetidos.
Para mais informações, consulte Processar ações do teclado.
Atalhos
Os 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 dos usuários pode ser frustrante. Além disso, atalhos para tarefas específicas usadas com frequência agradam usuários avançados. Os atalhos facilitam o uso e diferenciam o app de apps que não têm atalhos.
Alguns atalhos comuns incluem Ctrl+S (salvar), Ctrl+Z (desfazer) e Ctrl+Shift+Z (refazer). Para conferir 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 saber mais, consulte os tópicos abaixo:
Stylus
Muitos dispositivos de tela grande vêm equipados com uma stylus. Os apps Android processam stylus como entrada de tela touchscreen. Alguns dispositivos também podem ter uma prancheta de desenho USB ou Bluetooth, como o Wacom Intuos (link em inglês). Os apps Android podem receber entrada por Bluetooth, mas não por USB.
Para acessar objetos de stylus MotionEvent
, adicione o modificador pointerInteropFilter
a uma superfície de desenho. Implemente uma classe ViewModel
com um método que
processa 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_STYLUS
ouTOOL_TYPE_ERASER
, dependendo da ferramenta que fez contato com a tela.MotionEvent#getPressure()
informa a pressão física aplicada à stylus, se compatível.MotionEvent#getAxisValue()
comMotionEvent.AXIS_TILT
eMotionEvent.AXIS_ORIENTATION
fornecem a inclinação física e a orientação da stylus, se compatível.
Pontos históricos
O Android agrupa eventos de entrada e os envia uma vez por frame. Uma stylus pode
relatar eventos em frequências muito maiores do que a tela. Ao criar
apps de desenho, verifique eventos que podem estar no passado recente 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.
O Android cancela eventos de toque da palma da mão enviando 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 mais antigas 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
é insuficiente 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 anotação é solicitada, o app precisa criar uma
nota vazia pronta para entrada da stylus. Quando o usuário quiser escrever em uma imagem
(como uma captura de tela ou imagem salva), o app será iniciado com ClipData
contendo um ou mais itens com URIs content://
. O app precisa criar uma
nota que use a primeira imagem anexada como uma imagem de 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
[TBD remove section.]
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
i
para editar e adicionar--ash-enable-palette
a 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.
- Saia e faça login novamente
Agora você vai ver um menu da stylus 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. Você vai encontrar a opção Anotar imagem na notificação. O app vai iniciar com a imagem pronta para ser anotada.
Compatibilidade com mouse e touchpad
Geralmente, a maioria dos apps precisa processar apenas três eventos grandes com foco na tela: clicar 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 detalhes sobre como criar menus de contexto, consulte Criar um menu contextual.
Passar cursor
Você pode fazer com que os layouts do app pareçam bem-acabados e mais fáceis de usar processando eventos de passar o cursor. Isso é especialmente verdadeiro para componentes personalizados:
Os 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 é válido para dispositivos desktop, tablets, smartphones e dispositivos 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.
Considerações especiais para o ChromeOS
- Lembre-se de solicitar permissão com
requestDragAndDropPermissions()
para acessar itens arrastados de fora do app. Um item precisa ter a flag
View.DRAG_FLAG_GLOBAL
para ser arrastado para outros apps.Consulte Iniciar um evento de arrasto
Compatibilidade avançada com ponteiro
Os apps que processam entradas avançadas 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 e assim por diante dePointerEvent#changes
PointerEventType
: ações do ponteiro, como pressionar, mover, rolar e liberar
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 jogos do Android para processar controles de jogos. Consulte Suporte a controles de jogos.
Os botões do controle do 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 o pressionamento do botão 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 ativar automaticamente a rolagem com dois dedos no touchpad, a rolagem da roda do mouse e o mapeamento de coordenadas de exibição brutas para coordenadas de janela. Geralmente, 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" />