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:
- Testar o suporte básico do teclado, como 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/pausar em apps de música.
- Adicionar atalhos de teclado padrão, quando relevante. Por exemplo, Ctrl + Z para desfazer e Ctrl + S para salvar.
- Testar interações básicas do mouse com o botão direito 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 visualizações personalizadas
- Testar dispositivos de entrada específicos do app, como a stylus para apps de desenho, controles de jogos e controles de MIDI para apps de música.
- Considerar 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 uma boa experiência 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. Também pode ser essencial para usuários com necessidades de acessibilidade em smartphones, tablets, dispositivos dobráveis e dispositivos desktop.
Para muitos apps, a navegação simples pelas teclas de seta e Tab é o suficiente e
é gerenciada automaticamente pelo framework do Android. Por exemplo, uma visualização de
um Button
é focalizável por padrão, e a navegação pelo teclado geralmente funciona
sem precisar de mais código. Para ativar a navegação pelo teclado para visualizações
não focalizáveis por padrão, os desenvolvedores precisam marcá-las como focalizáveis, o que pode
ser feito de maneira programática ou em XML, conforme mostrado abaixo. Consulte
Processamento de foco
para ver mais informações.
Kotlin
yourView.isFocusable = true
Java
yourView.setFocusable(true);
Como alternativa, você pode definir o atributo focusable
no arquivo de layout:
android:focusable="true"
Após a ativação do foco, o framework do Android cria um mapeamento de navegação para todas as visualizações focalizáveis com base na posição delas. Isso geralmente funciona conforme o esperado, e não é necessário realizar qualquer outra ação. Quando o mapeamento padrão não está correto para as necessidades de um app, ele pode ser substituído da seguinte maneira:
Kotlin
// Arrow keys yourView.nextFocusLeftId = R.id.view_to_left yourView.nextFocusRightId = R.id.view_to_right yourView.nextFocusTopId = R.id.view_above yourView.nextFocusBottomId = R.id.view_below // Tab key yourView.nextFocusForwardId = R.id.next_view
Java
// Arrow keys yourView.setNextFocusLeftId(R.id.view_to_left); yourView.setNextFocusRightId(R.id.view_to_left); yourView.setNextFocusTopId(R.id.view_to_left); yourView.setNextFocusBottomId(R.id.view_to_left); // Tab key yourView.setNextFocusForwardId(R.id.next_view);
É recomendável tentar acessar todas as funcionalidades do app antes de cada lançamento, usando apenas o teclado. Facilite o acesso às ações mais comuns sem precisar do mouse ou da 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), por exemplo, um EditText
, os apps
precisam se comportar como esperado em dispositivos de tela grande sem trabalho extra do
desenvolvedor. 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úsica que iniciam/interrompem a reprodução com a tecla de espaço e jogos que controlam o movimento com as teclas w, a, s e d.
A maioria dos apps modifica o callback
onKeyUp()
e adiciona o comportamento esperado para cada código de tecla recebido, conforme mostrado
abaixo:
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) } }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER) { sendMessage(); return true; } else if (KeyEvent.KEYCODE_SPACE){ playOrPauseMedia(); return true; } else { return super.onKeyUp(keyCode, event); } }
Um evento onKeyUp
ocorre quando uma chave é liberada. O uso desse callback evita
que os apps precisem processar vários eventos onKeyDown
se uma tecla é pressionada
ou liberada lentamente. Jogos e apps que querem saber quando uma tecla é
pressionada ou que esperam que os usuários mantenham as teclas pressionadas no teclado, podem procurar
o evento
onKeyDown()
e processar eventos onKeyDown
repetidos.
Para ver mais informações sobre como oferecer compatibilidade com teclado, consulte Processar ações do teclado.
Atalhos
Os atalhos comuns Ctrl, Alt e Shift são esperados ao usar um teclado físico. Se um app não os implementa, a experiência dos usuários pode se tornar frustrante. Além disso, atalhos para tarefas específicas usadas com frequência agradam usuários avançados. Os atalhos facilitam o uso e a diferenciação 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 um exemplo de alguns atalhos mais avançados, consulte a lista de teclas de atalho do player de mídia VLC (link em inglês).
Os atalhos podem ser implementados usando
dispatchKeyShortcutEvent()
.
Isso intercepta todas as combinações de tecla meta (Alt, Ctrl e Shift) para um determinado
código de tecla. Para conferir se há uma tecla meta específica, use
KeyEvent.isCtrlPressed()
,
KeyEvent.isShiftPressed()
,
KeyEvent.isAltPressed()
ou
KeyEvent.hasModifiers()
.
A separação do código de atalho de outro processo de pressionamento de tecla (como onKeyUp()
e onKeyDown()
) pode facilitar a manutenção do código e possibilita a aceitação
padrão das teclas meta sem precisar implementar manualmente as verificações de tecla meta em
todos os casos. Autorizar todas as combinações de tecla meta também pode ser mais conveniente para
usuários acostumados com diferentes layouts de teclado e sistemas operacionais.
Kotlin
override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean { return when (event.keyCode) { KeyEvent.KEYCODE_O -> { openFile() // Ctrl+O, Shift+O, Alt+O true } KeyEvent.KEYCODE_Z-> { if (event.isCtrlPressed) { if (event.isShiftPressed) { redoLastAction() // Ctrl+Shift+Z pressed true } else { undoLastAction() // Ctrl+Z pressed true } } } else -> { return super.dispatchKeyShortcutEvent(event) } } }
Java
@Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_O) { openFile(); // Ctrl+O, Shift+O, Alt+O return true; } else if(event.getKeyCode() == KeyEvent.KEYCODE_Z) { if (event.isCtrlPressed()) { if (event.isShiftPressed()) { redoLastAction(); return true; } else { undoLastAction(); return true; } } } return super.dispatchKeyShortcutEvent(event); }
Você também pode implementar atalhos em onKeyUp()
pela verificação de
KeyEvent.isCtrlPressed()
,
KeyEvent.isShiftPressed()
ou
KeyEvent.isAltPressed()
como explicado acima. Isso pode ser mais fácil de manter se o
comportamento meta for mais uma modificação em um comportamento do app do que um atalho.
Por exemplo, quando W significa "avançar" e Shift + W significa "avançar correndo".
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when(keyCode) { KeyEvent.KEYCODE_W-> { if (event.isShiftPressed) { if (event.isCtrlPressed) { flyForward() // Ctrl+Shift+W pressed true } else { runForward() // Shift+W pressed true } } else { walkForward() // W pressed true } } else -> super.onKeyUp(keyCode, event) } }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_W) { if (event.isShiftPressed()) { if (event.isCtrlPressed()) { flyForward(); // Ctrl+Shift+W pressed return true; } else { runForward(); // Shift+W pressed return true; } } else { walkForward(); return true; } } return super.onKeyUp(keyCode, event); }
Stylus
Muitos dispositivos de tela grande vêm com uma stylus, e os apps Android processam isso como entrada na tela touchscreen. Alguns dispositivos também podem ter uma prancheta de desenho USB ou Bluetooth, como o Wacom Intuos (link em inglês). Apps Android podem receber entradas por Bluetooth, mas não funcionam com a entrada USB.
Um evento da stylus é relatado como um evento de touchscreen via
View.onTouchEvent()
ou
View.onGenericMotionEvent()
e contém um
MotionEvent.getSource()
do tipo
SOURCE_STYLUS
.
O MotionEvent
também contém outros dados:
MotionEvent.getToolType()
retornaTOOL_TYPE_FINGER
, TOOL_TYPE_STYLUS ouTOOL_TYPE_ERASER
, dependendo da ferramenta que fez o contato com a superfície.MotionEvent.getPressure()
representa a pressão física aplicada à stylus, se compatível.MotionEvent.getAxisValue()
comMotionEvent.AXIS_TILT
eMotionEvent.AXIS_ORIENTATION
que podem ser usados para ler 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, é importante conferir 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.
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
. A partir do Android 13, por conveniência, o
sistema define FLAG_CANCELED
para eventos ACTION_CANCEL
, mas as versões anteriores
não.
Android 12
No Android 12 (API de nível 32) 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 Android:
<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, o usuário pode selecioná-lo como o app de anotações
padrão. Quando uma nova anotação for solicitada, o app precisará 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 é 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 pode 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:
- Troque 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 ver 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 gerenciar eventos de clique com o botão direito do mouse,
os apps precisam registrar um
View.OnContextClickListener
.
Para saber detalhes sobre como criar um menu de contexto, consulte
Criação de menus de contexto.
Kotlin
yourView.setOnContextClickListener { showContextMenu() true }
Java
yourView.setOnContextClickListener(v -> { showContextMenu(); return true; });
Passar cursor
Os desenvolvedores podem fazer com que seus layouts de apps pareçam bem-acabados e fáceis de usar pelo processamento de eventos de passar o cursor. Isso é especialmente verdadeiro para visualizações personalizadas. Os dois exemplos mais comuns são:
- indicar aos usuários se um elemento tem comportamento interativo, 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.
Kotlin
// Change the icon to a "hand" pointer on hover, // Highlight the view by changing the background. yourView.setOnHoverListener { view, _ -> addVisualHighlighting(true) view.pointerIcon = PointerIcon.getSystemIcon(view.context, PointerIcon.TYPE_HAND) false // listener did not consume the event. }
Java
yourView.setOnHoverListener((view, event) -> { addVisualHighlighting(true); view.setPointerIcon(PointerIcon .getSystemIcon(view.getContext(), PointerIcon.TYPE_HAND)); return true; });
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.
Os desenvolvedores precisam considerar se os usuários vão arrastar itens para o app. Alguns exemplos comuns incluem: editores de fotos provavelmente recebem fotos, players de áudio recebem arquivos de áudio e programas de desenho recebem imagens.
Para oferecer suporte ao recurso de arrastar e soltar, siga a documentação desse recurso do Android e confira esta postagem do blog do ChromeOS (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 sinalização
View.DRAG_FLAG_GLOBAL
para ser arrastado para outros apps.
Compatibilidade avançada com ponteiro
Os apps que lidam com entradas avançadas de mouse e touchpad precisam seguir a
documentação do Android para
View.onGenericMotionEvent()
e usar
MotionEvent.getSource()
para distinguir entre
SOURCE_MOUSE
e
SOURCE_TOUCHSCREEN
.
Examine o MotionEvent
para implementar o comportamento necessário:
- O movimento gera eventos de
ACTION_HOVER_MOVE
. - Os botões geram eventos de
ACTION_BUTTON_PRESS
eACTION_BUTTON_RELEASE
. Também é possível conferir o estado atual de todos os botões do mouse/trackpad usandogetButtonState()
. - A rolagem da roda do mouse gera eventos
ACTION_SCROLL
.
Controles de jogos
Alguns dispositivos Android de tela grande oferecem suporte para até quatro controles de jogo. Para processar esses controles, os desenvolvedores precisam usar as APIs de controle de jogos padrão do Android. Consulte Compatibilidade com controles de jogos.
Os botões são mapeados para valores comuns após um mapeamento comum. Infelizmente, 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 pressionamentos do botão do gamepad para saber mais.
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" />
Outros recursos
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Melhorar o suporte à stylus em um app Android
- Editores de texto personalizados
- Suporte para tablets e telas grandes