Por que eu devo ler este artigo:

Uma interface com usuário bem elaborada é fundamental para o sucesso da app desenvolvida.

Abordaremos neste artigo as boas práticas em relação a entrada de dados no Android para apresentar métodos e técnicas de inserção de valores através do uso de teclado e gestos, tendo como objetivo melhorar experiência para o usuário do dispositivo.

De acordo com relatórios de uma pesquisa feita no ano de 2013 pela Google, cerca de 30% das pessoas preferem ficar sem televisão do que smartphone.

O uso do smartphone é tão intenso que está ocorrendo uma enorme transformação em tarefas comuns como, por exemplo, se locomover pelo mundo associada à tarefa de encontrar um determinado local desejado.

Cerca de 89% dos usuários de smartphone buscam informações de locais e 90% tomam decisões de ir ou não ao local com base nas informações pesquisadas.

A grande maioria (92%) dos usuários de dispositivos móveis utilizam seus aparelhos para interagir com o mundo externo através do uso de alguma rede social e alguns desses usuários que acessam uma rede social fazem essa tarefa mais de uma vez ao dia.

Para utilizar o smartphone, o usuário não tem horário e nem lugar. Apesar da maior parte do tempo a utilização do aparelho ser feita em casa, também ocorre uso no trabalho, restaurante, lojas e inclusive no trânsito.

Sendo assim, a interação tem que ser imediata e instantânea, pois o usuário pode ter um tempo muito curto para manusear o dispositivo, sendo no instante que o semáforo fecha até abrir novamente.

O smartphone se tornou um enorme concorrente para outras tarefas, pois 90% das pessoas utilizam o aparelho enquanto fazem outras atividades como ouvir música, assistir TV, usar Internet, ler jornal, revista ou livro, jogar videogames.

Essa necessidade de rapidez de interação entre o usuário e o smartphone tem despertado uma atenção maior dos desenvolvedores e analistas, pois uma interface amigável e uma boa prática de entrada de dados podem ser decisivas para que a sua app obtenha um número maior de downloads do que seu concorrente.

Toques e gestos usados

O sistema Android é baseado em dispositivos com tela sensível ao toque, também chamado de touchscreen. No Android podemos contar com uma série de APIs que nos ajudam a reconhecer e disparar eventos através da ação do usuário ao tocar na tela.

Uma atenção que o desenvolvedor deverá ter ao desenvolver é que a sua aplicação não deverá depender somente do toque. Devemos ter a consciência de que a interação através de touch pode não estar sempre acessível para todos os tipos de usuários.

Para oferecer ao usuário uma experiência única e mais intuitiva, devemos estar atentos às especificações do Android para interações através de gestos e toques. No site de apoio do desenvolvedor Android podemos encontrar um manual com dicas para o design poder melhorar ainda mais a experiência do usuário.

Dentre uma grande variedade de toques, podemos destacar alguns como gestos comuns que são utilizados com mais frequência. O gesto mais comum de ser utilizado é o touch, que é disparado quando o usuário dá apenas um toque na tela, podendo ocorrer com um dedo ou mais.

Quando um gesto é executado, temos dois momentos de detecção da ação do usuário na tela do dispositivo:

  • Coleta de dados no momento em que o dedo é encostado no display;
  • Interpretar o estímulo do usuário para ver se ele atende aos requisitos de algum evento de gesto.

Classes de suporte aos gestos

Nos trechos a seguir veremos exemplos da classe GestureDetectorCompat, responsável por decifrar qual o tipo de gesto foi realizado pelo usuário e o MotionEventCompat, que deve ser utilizado em conjunto com o GestureDetectorCompat, e que é o responsável por monitorar quando algo é encostado na tela, tais como: mouse, caneta, dedo e trackball. Outra função importante da classe MotionEventCompat é detectar as coordenadas (X, Y) do toque.

As duas classes citadas foram incluídas a partir da versão 1.6 do Android, portanto daí em diante temos uma compatibilidade de detecção de gestos.

Coletando dados

Quando um usuário encosta um ou mais dedos na tela, ele dispara a chamada ao evento onTouchEvent() que irá informar que algum gesto de toque foi realizado.

Cada parâmetro da ação efetuada pelo usuário ao tocar na tela (posição, pressão, tamanho, a adição de outro dedo, etc.) será então processado.

No momento em que o usuário encosta o dedo no touch do aparelho, o sistema já é capaz de rastrear a posição do dedo e o evento é concluído somente quando o toque é encerrado. Ao longo desta interação, o MotionEvent fornece os detalhes do toque realizado para o onTouchEvent(). O seu aplicativo pode usar os dados detalhados fornecidos pelo MotionEvent para determinar qual o tipo de toque foi disparado pelo usuário.

Detectando gestos comuns

A classe GestureDetector apoia a detecção de gestos comuns. Esses gestos incluem o onDown(), onLongPress(), onFling(). Para que a GestureDetector reconheça os eventos disparados, ela deve trabalhar em conjunto com o onTouchEvent(). Como podemos ver na Figura 1, quando o usuário dá um toque simples na tela, é reconhecido o evento de onDown() pela classe GestureDetector.

Representação do evento onDown
Figura 1. Representação do evento onDown

Quando o usuário dá um toque na tela e mantém o dedo pressionado por alguns instantes, é disparado o evento onLongPress, como apresentado na Figura 2.

Representação do evento onLongPress
Figura 2. Representação do evento onLongPress

Na Figura 3 é apresentado o evento onFling. O mesmo é executado quando o usuário desliza o dedo sobre a tela em um sentido horizontal.

Representação do evento onFling
Figura 3. Representação do evento onFling

Quando criamos um objeto do tipo GestureDetectorCompat, é necessário implementar em uma classe o método GestureDetector.OnGestureListener, pois esse notifica aos usuários quando um evento de toque especial ocorreu.

Acionando um movimento

Sempre que o usuário executa ou altera uma ação de toque como mudança de posição, pressão ou tamanho, é acionado um novo onTouchEvent(). Todos os dados são registrados pela onMotionEvent e passados como parâmetro para a função do onTouchEvent().

Para que o Android entenda melhor se o toque do usuário é com movimento ou não, uma margem de variação do toque que é desconsiderada. Isso ocorre porque em um toque simples pode ocorrer uma variação pequena da posição do cursor, visto que o dedo do usuário pode variar em relação à pressão e posição.

O seu aplicativo pode acompanhar o movimento de diversas maneiras, dependendo da aplicação do mesmo, por exemplo:

  • A posição inicial e final do toque, ou seja, se a posição inicial (A) é igual a posição final (B) o toque é não movimentado;
  • O deslocamento do ponteiro sobre a tela, capturando as coordenadas X e Y do cursor a cada movimentação;
  • A velocidade que o ponteiro percorre a tela do aparelho.

Quando um gesto é movimentado, esse poderá ser baseado na velocidade ou na direção em que o gesto acontece. Muitas vezes a velocidade é um fator determinante para acompanhar as características de um gesto ou até mesmo saber se o gesto realmente ocorreu.

O Android conta também com classes específicas para trabalhar com a velocidade do ponteiro: VelocityTracker e VelocityTrackerCompat. A classe VelocityTracker ajuda a controlar a velocidade dos eventos de toque.

O uso das classes de velocidade se torna bastante útil quando o aplicativo depende da velocidade para fazer os devidos critérios para a execução do gesto.

Animando um gesto Scroll

No Android, a rolagem é implementada através da classe ScrollView. Se você tiver em seu layout algo que vá além dos limites da tela, estes devem ser alinhados em um ScrollView para que o aplicativo forneça a opção de rolagem gerenciada automaticamente pela própria estrutura do sistema. Somente em casos específicos devemos aplicar um scroller customizado.

Você pode usar scrollers (Scroller ou OverScroller) na coleta dos dados necessários para produzir uma animação de rolagem. O Scroller e o OverScroller são semelhantes, mas o OverScroller inclui métodos para indicar que o usuário já atingiu as bordas do dispositivo após uma rolagem.

Os scrollers são utilizados para animar a rolagem do conteúdo, através de parâmetros físicos, como: atrito, velocidade, deslocamento, etc.

Gestos multi-touch

Um gesto é considerado multi-touch quando o usuário utiliza dois ou mais dedos para executar uma ação no display do dispositivo. Esses gestos podem ser chamados também de gestos de múltiplos toques.

Para que o sistema compreenda os vários toques simultâneos na tela, alguns eventos são disparados automaticamente, sendo eles:

  • ACTION_DOWN: Esse evento é disparado sempre que o usuário coloca um dedo sobre a tela do aparelho. No MotionEvent podemos obter os dados referentes a esse ponteiro, basta pegar os dados do índice 0;
  • ACTION_POINTER_DOWN: Para ponteiros extras, além do primeiro, é disparado esse evento. Os dados dos apontadores extras são obtidos através do getActionIndex();
  • ACTION_MOVE: O evento é chamado quando o gesto é movimentado, ou seja, quando o apontador se desloca pela tela;
  • ACTION_POINTER_UP: Ao terminar a ação de um toque na tela, sendo assim, quando um dedo é retirado da tela, é disparado o evento em questão;
  • ACTION_UP: Quando todos os ponteiros são retirados do touch disparamos o evento citado.

Em um evento do tipo MotionEvent, temos a possibilidade de controlar os indicadores de uma maneira individual para cada ponteiro através de sua ID:

  • Index: Cada ponteiro que toca na tela tem os seus dados armazenados em uma matriz. O índice de um ponteiro é a sua posição dentro destas informações armazenadas.

A maioria dos métodos MotionEvent podem ser usados para interagir com ponteiros. Nestes casos, o index do mesmo é que deverá ser utilizado como parâmetro, ao invés de utilizar a ID;

  • ID: Cada ponteiro recebe um ID permanente, o que possibilita ao aplicativo acompanhar um ponteiro individualmente ao longo de todo o gesto.

O índice de um ponteiro pode mudar de um evento para o outro, mas a identificação do ponteiro é garantida que permanece constante desde que o ponteiro permaneça ativo.

Para a obtenção da identificação do ponteiro devemos utilizar o método getPointerId(), sendo assim é possível saber todas as ações do ponteiro em qualquer evento. Após obter o ID do ponteiro, fica fácil saber qual é o seu índice, basta apenas passar a identificação como parâmetro para o método findPointerIndex() que será retornado o index.

Arrastar um objeto

Uma ação comum e bastante utilizada por muitos usuários é a de arrastar objetos pela tela do dispositivo. O trecho a seguir esclarece um pouco mais sobre o evento. O toque na tela como opção de arrastar necessita de um controle mais efetivo sobre o evento, mesmo que sejam adicionados novos ponteiros.

Para que o seu aplicativo possa identificar os múltiplos ponteiros utilizados para mover uma imagem, devemos possibilitar que a nossa aplicação faça o reconhecimento dos demais ponteiros, caso contrário ao movimentar o segundo ponteiro a imagem se deslocaria instantaneamente para a posição do segundo ponteiro.

Para que não ocorra a confusão entre ponteiros, ela controla os eventos ACTION_POINTER_DOWN e ACTION_POINTER_UP que foram descritos anteriormente no manuseio gestos Multi-Touch. ACTION_POINTER_DOWN e ACTION_POINTER_UP são passados ​​para o método onTouchEvent(), esse método é disparado sempre que um ponteiro, que não seja o primário, inicia ou termina um gesto.

O ACTION_POINTER_UP sempre é chamado quando o ponteiro não está mais sobre a tela, ou seja, quando o usuário termina o seu gesto. O ACTION_MOVE é utilizado sempre que há um deslocamento de um ponteiro pela tela para calcular a distância X e Y percorrida pelo mesmo. O ACTION_POINTER_DOWN será invocado sempre que um ponteiro encosta sobre a tela do dispositivo, ou seja, quando um gesto é iniciado.

Toque em um ViewGroup

O toque em um ViewGroup deve receber um cuidado especial, pois em um ViewGroup podemos ter outros ViewGroup aninhados. Esses ViewGroup filhos podem ter eventos associados a eles, isso dificulta ainda mais a precisão de qual evento será disparado pelo gesto.

O método onInterceptTouchEvent() é chamado sempre que um evento de toque é detectado na superfície de um ViewGroup, incluindo na superfície de um ViewGroup filho.

O método onInterceptTouchEvent() dá ao ViewGroup pai a chance de ver qualquer evento de toque antes mesmo que seus filhos. Se o método do onInterceptTouchEvent() retornar verdadeiro, a ação do ViewGroup filho que recebeu previamente o evento de toque receberá um ACTION_CANCEL, e os eventos a partir daí são enviados para o método pai onTouchEvent() para a execução das ações habituais.

O onInterceptTouchEvent() também pode retornar false e simplesmente escutar os eventos disparados pelos filhos, estes por sua vez irão lidar com os eventos com o seu próprio método onTouchEvent().

Manuseio de entrada do teclado

O sistema Android mostra na tela uma entrada de teclado sempre quando um campo do tipo texto da sua interface recebe o foco. Para fornecer a melhor experiência ao usuário, você pode especificar características sobre o tipo de entrada que você espera (um número de telefone ou endereço de e-mail) e como o método de entrada deve se comportar.

Além dos métodos de entrada na tela, o Android também dá suporte a teclados físicos, por isso é importante que a sua app esteja preparado para essa maneira de receber os dados do usuário.

Cada campo de texto espera um certo tipo específico de entrada, tais como endereço de e-mail, número de telefone, ou apenas números.

Por isso, é importante que você identifique o tipo de entrada para cada campo em seu aplicativo para que o sistema exiba o método de entrada flexível apropriado para aquele campo.

Além dos tipos de botões disponíveis como um método de entrada, você deve especificar também se o método de entrada fornece sugestões de correção ortográfica, capitaliza novas sentenças e se substitui o botão de quebra de linha (ENTER) com um botão de ação, como Concluído ou Avançar.

Especifique o tipo de teclado

Você deve sempre declarar o método de entrada para os seus campos de texto, adicionando o atributo inputType ao elemento do Android. Por exemplo, se você quiser um método de entrada para um número de telefone, use o valor phone, como demonstrado na Listagem 1.


<EditText
  android:id = "@+id/telefone"
  android:layout_width = "fill_parent"
  android:layout_height = "wrap_content"
  android:hint = "Numero de Telefone"
  android:inputType = "phone"  />
Listagem 1. Exemplo de uso do tipo de input phone

Veja na Figura 4 um exemplo de um teclado numérico para inserção de um número de telefone.

Teclado numérico
Figura 4. Teclado numérico

Caso o seu campo de texto seja uma senha, utilize o valor textPassword, assim o campo de texto não deixará visível a entrada do usuário (vide Listagem 2).


<EditText
  android:id = "@+id/senha"
  android:hint = "Senha de Acesso"
  android:inputType = "textPassword"
  ... />
Listagem 2. Exemplo de uso do tipo de input textPassword

Veja na Figura 5 um exemplo de campo do tipo password.

EditText do tipo Password
Figura 5. EditText do tipo Password

Existem diversos valores possíveis documentados com o atributo android:inputType, e alguns dos valores podem ser combinados para especificar a aparência do método de entrada e comportamentos adicionais. Mais importante ainda, se o seu campo de texto é utilizado para entrada de texto básica (como para uma SMS), você deve habilitar autocorreção ortográfica com valor textAutoCorrect.

Podem haver casos onde você precise combinar diferentes comportamentos e estilos de método de entrada. Por exemplo, um campo pode necessitar iniciar com a primeira letra em maiúsculo e também deve haver a correção ortográfica, como demonstra o código da Listagem 3.


<EditText
  android:id = "@+id/mensagem"
  android:layout_width = "wrap_content"
  android:layout_height = "wrap_content"
  android:inputType = "textCapSentences|textAutoCorrect"
  ... />
Listagem 3. Exemplo de uso da autocorreção e letra maiúscula

A maioria dos métodos de entrada através do teclado deve fornecer um botão de ação ao usuário, no canto inferior, de acordo com a necessidade do campo de texto atual.

Por padrão, o sistema usa este botão de ação como Avançar ou Concluído, a menos que o seu campo de texto permita que o texto tenha múltiplas linhas (como android:inputType = "textMultiLine").

Neste caso, o botão de ação é um retorno. Você poderá especificar ações adicionais que são mais apropriadas para o seu campo de texto tais como Enviar.

Para especificar o botão de ação do teclado, use o atributo android:imeOptions com um valor de ação, como actionSend ou actionSearch, como apresenta a Listagem 4.


<EditText
  android:id = "@+id/pesquisa"
  android:layout_width = "fill_parent"
  android:layout_height = "wrap_content"
  android:inputType = "text"
  android:imeOptions = "actionSend"  />
Listagem 4. Exemplo de declaração de botão para ação do teclado

Veja nas Figuras 6 e 7 os botões de ação para enviar e pesquisar respectivamente.

Botão Send
Figura 6. Botão Send
Botão Search
Figura 7. Botão Search (botão de Pesquisa)

Visibilidade do método de entrada

Quando o foco de entrada se move para dentro ou fora de um campo de texto editável, é exibido ou escondido o método de entrada conforme apropriado.

O sistema também toma decisões sobre como a interface do usuário e o campo de texto aparecem acima do método de entrada.

Por exemplo, quando o espaço vertical no display é limitado, o campo de texto pode preencher todo o espaço acima do método de entrada. Para a maioria dos aplicativos, esses comportamentos padrão são necessários.

Em alguns casos, porém, você pode querer controlar mais diretamente a visibilidade do método de entrada e especificar como você gostaria que seu layout apareça quando o método de entrada é visível.

Embora o Android dê foco para o primeiro campo de texto em seu layout quando a atividade é iniciada, ele não mostra o método de entrada. Esse comportamento é apropriado porque a inserção de texto pode não ser a tarefa principal na atividade.

No entanto, se a entrada de texto é de fato a principal tarefa (como efetuar login), então você provavelmente vai querer que o método de entrada apareça por padrão.

Para mostrar o método de entrada quando a sua atividade inicia, adicione o android:windowSoftInputMode à tag do elemento desejado com o valor stateVisible, conforme a Listagem 5.


<application ... >
  <activity
      android:windowSoftInputMode="stateVisible" ... >
      ...
  </activity>
</application>
Listagem 5. Exemplo de declaração da visibilidade da atividade

Caso o dispositivo do usuário tenha um teclado físico conectado, o método de entrada virtual não aparece no display do aparelho.

Quando o método de entrada é exibido na tela, a quantidade de espaço disponível para interface do usuário do seu aplicativo é reduzida. O sistema faz uma decisão sobre a forma como deve ajustar a parte visível de sua interface, mas pode não acertar.

Para garantir o melhor comportamento para o aplicativo, você deve especificar como você gostaria que o sistema exibisse sua interface no espaço restante.

Por exemplo, para garantir que o sistema redimensione seu layout para o espaço disponível que garante que todo o conteúdo do seu layout seja acessível (embora provavelmente necessite de deslocamento) – use o adjustResize, de acordo com a Listagem 6.


<application ... >
  <activity
      android:windowSoftInputMode="adjustResize" ... >
      ...
  </activity>
  ...
</application>

/*Você pode combinar a especificação de ajuste com o método de entrada de especificar a
   visibilidade inicial: */

<activity
      android:windowSoftInputMode="stateVisible|adjustResize" ... >
      ...
</activity>
Listagem 6. Código que demonstra como configurar o auto ajuste do layout

Especificar "adjustResize" é importante se a sua interface inclui controles que o usuário possa precisar para acessar imediatamente após ou durante a execução de entrada de texto.

Suporte de navegação Teclado

Um teclado não só oferece um modo conveniente para a entrada de texto, mas também oferece uma maneira para os usuários navegarem e interagirem com seu aplicativo. Embora a maioria dos dispositivos portáteis usem o toque como o principal modo de interação, muitos usuários gostam de acoplar o teclado físico.

Todos os componentes interativos fornecidos pela estrutura do Android (como Button e EditText) permitem a interação a partir do teclado. Isso significa que os usuários podem navegar com dispositivos de controle, como um D-pad (vide Figura 8) e quando o componente da interface receber o foco, pode haver mudanças ou não em suas características.

D-pad
Figura 8. D-pad

Para testar seu aplicativo, instale em um dispositivo que oferece um teclado físico. Conecte um teclado Bluetooth ou um teclado USB (embora nem todos os dispositivos suportem acessórios USB).

Se você encontrar quaisquer casos em que navegação com os principais controles de direção ou tab não fizer o que você espera, especifique onde o foco deve ir em seu layout.

Quando um usuário navega em seu aplicativo usando a tecla tab do teclado, o sistema passa o foco de entrada entre elementos com base na ordem em que aparecem no layout.

Se você usar um layout em relação, por exemplo, e a ordem dos elementos na tela é diferente da ordem no arquivo, então você pode precisar especificar manualmente a ordem do foco.

No esquema a seguir, dois botões são alinhados com o lado direito e um campo de texto está alinhada à esquerda do segundo botão.

A fim de passar o foco do primeiro botão para o campo de texto e, em seguida, para o segundo botão, o layout precisa definir explicitamente a ordem do foco para cada um dos elementos focusable com o Android: atributo nextFocusForward (ver Listagem 7).


<RelativeLayout ...>
  <Button
      android:id="@+id/button1"
      android:layout_alignParentTop="true"
      android:layout_alignParentRight="true"
      android:nextFocusForward="@+id/editText1"
      ... />
  <Button
      android:id="@+id/button2"
      android:layout_below="@id/button1"
      android:nextFocusForward="@+id/button1"
      ... />
  <EditText
      android:id="@id/editText1"
      android:layout_alignBottom="@+id/button2"
      android:layout_toLeftOf="@id/button2"
      android:nextFocusForward="@+id/button2"
      ...  />
  ...
</RelativeLayout>
Listagem 7. Exemplo de código com mudança de foco

Os usuários também podem navegar no seu aplicativo usando as setas do teclado. O sistema fornece um melhor palpite a respeito de qual deve ser o foco em uma determinada direção com base no layout das visualizações na tela. Às vezes, porém, o sistema pode errar.

Se o sistema não passar o foco para a visualização apropriada quando se navega em uma determinada direção, o desenvolvedor deve especificar qual sequência deve receber o foco com os seguintes atributos:


  android: nextFocusUp
  android: nextFocusDown
  android: nextFocusLeft
  android: nextFocusRight

Cada atributo designa o próximo fim de receber o foco quando o usuário navega nessa direção, conforme especificado pelo ID, como na Listagem 8.


<Button
  android:id="@+id/button1"
  android:nextFocusRight="@+id/button2"
  android:nextFocusDown="@+id/editText1"
  ... />
<Button
  android:id="@id/button2"
  android:nextFocusLeft="@id/button1"
  android:nextFocusDown="@id/editText1"
  ... />
<EditText
  android:id="@id/editText1"
  android:nextFocusUp="@id/button1"
  ...  />
Listagem 8. Código com organização da sequência da mudança de foco

Quando o usuário dá o foco a um campo de texto editável, como um elemento EditText, e o usuário tem um teclado físico anexado, toda a entrada é tratada pelo sistema. Se, no entanto, você quiser de interceptar ou diretamente lidar com a entrada de teclado, você pode fazê-lo através da aplicação de métodos de retorno de chamada a partir da interface KeyEvent.Callback, como onKeyDown() e onKeyMultiple().

São inúmeras as possibilidades de customização do comportamento de sua aplicação Android na interação com o usuário. Este é sem dúvida um campo muito rico para estudos. Isso sem falar das novas possibilidades de interação que sempre são lançadas a cada nova versão do Android.

Confira também