Visão geral dos eventos de entrada

No Android, há mais de uma maneira de interceptar os eventos da interação de um usuário com o aplicativo. Ao considerar os eventos dentro da interface do usuário, a abordagem é capturar os eventos de um objeto View específico com que o usuário interage. A classe View fornece os meios para fazer isso.

Dentro das várias classes View que você usará para compor o layout, é possível notar vários métodos públicos de callback que parecem úteis para eventos de IU. Esses métodos são chamados pela biblioteca do Android quando a ação respectiva ocorre neste objeto. Por exemplo, quando uma View (como um botão) é tocada, o método onTouchEvent() é chamado neste objeto. No entanto, para interceptar isso, você precisa estender a classe e modificar o método. No entanto, estender todos os objetos de View para lidar com tal evento não seria algo prático. É por isso que a classe View também contém uma coleção de interfaces aninhadas com retornos de chamada que podem ser definidas com muito mais facilidade. Essas interfaces, chamadas de listeners de evento, são sua passagem para capturar a interação do usuário com a IU.

Geralmente, os listeners de evento são usados para escutar a interação do usuário. No entanto, há casos em que você pode querer estender uma classe View para criar um componente personalizado. Talvez você queira estender a classe Button para deixar algo mais sofisticado. Neste caso, você poderá definir os comportamentos de evento padrão para a classe usando gerenciadores de evento.

Listeners de evento

Um listener de evento é uma interface na classe View que contém um único método de callback. Esses métodos serão chamados pela biblioteca do Android quando a View a que o listener estiver registrado for ativada pela interação do usuário com o item na IU.

Inclusos nas interfaces do listener de evento estão os seguintes métodos de callback:

onClick()
De View.OnClickListener. Isso é chamado quando o usuário toca no item (no modo de toque) ou atribui foco ao item com as teclas de navegação ou o trackball e pressiona a tecla “enter” ou o pressiona no trackball.
onLongClick()
De View.OnLongClickListener. Isso é chamado quando o usuário mantém o item pressionado (no modo de toque) ou atribui foco ao item com as teclas de navegação ou o trackball e mantém pressionada a tecla “enter” ou o trackball (por um segundo).
onFocusChange()
De View.OnFocusChangeListener. Isso é chamado quando o usuário navega para ou do item usando as teclas de navegação ou o trackball.
onKey()
De View.OnKeyListener. Isso é chamado quando o usuário está com foco no item e pressiona ou solta uma tecla de hardware no dispositivo.
onTouch()
De View.OnTouchListener. Isso é chamado quando o usuário realiza uma ação qualificada como um toque de evento, incluindo o pressionamento, a liberação ou qualquer outro gesto de movimento na tela (dentro dos limites do item).
onCreateContextMenu()
De View.OnCreateContextMenuListener. Isso é chamado quando um menu de contexto está sendo construído (como resultado de um “clique longo”). Consulte a discussão sobre menus de contexto no guia do desenvolvedor de Menus.

Esses métodos são os únicos habitantes das respectivas interfaces. Para definir um desses métodos e lidar com seus eventos, implemente a interface aninhada na Activity ou defina-a como uma classe anônima. Em seguida, passe uma instância da sua implementação para o respectivo método View.set...Listener(). Por exemplo: chame setOnClickListener() e passe-o à implementação de OnClickListener.

O exemplo abaixo mostra como registrar um listener de clique para um botão.

Kotlin

protected void onCreate(savedValues: Bundle) {
    ...
    val button: Button = findViewById(R.id.corky)
    // Register the onClick listener with the implementation above
    button.setOnClickListener { view ->
        // do something when the button is clicked
    }
    ...
}

Java

// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = new OnClickListener() {
    public void onClick(View v) {
      // do something when the button is clicked
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // Capture our button from layout
    Button button = (Button)findViewById(R.id.corky);
    // Register the onClick listener with the implementation above
    button.setOnClickListener(corkyListener);
    ...
}

Você também pode achar mais conveniente implementar OnClickListener como parte da Activity. Isso evitará carga extra na classe e a alocação do objeto. Por exemplo:

Kotlin

class ExampleActivity : Activity(), OnClickListener {
  
    protected fun onCreate(savedValues: Bundle) {
        val button: Button = findViewById(R.id.corky)
        button.setOnClickListener(this)
    }

    // Implement the OnClickListener callback
    fun onClick(v: View) {
        // do something when the button is clicked
    }
}

Java

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // Implement the OnClickListener callback
    public void onClick(View v) {
      // do something when the button is clicked
    }
    ...
}

O callback onClick() no exemplo acima não tem valor de retorno, mas outros métodos de listener de evento podem retornar um booleano. O motivo depende do evento. Este é o motivo para os poucos que retornam:

  • onLongClick(): isso retorna um booleano para indicar se você consumiu o evento e se ele não deve ser levado adiante. Ou seja, retorne true para indicar que você processou o evento e ele não deve seguir adiante, ou retorne false caso você não tenha processado o evento ou ele deva continuar para qualquer outro listener de clique.
  • onKey(): isso retorna um booleano para indicar se você consumiu o evento e se ele não deve ser levado adiante. Ou seja, retorne true para indicar que você processou o evento e ele não deve seguir adiante, ou retorne false caso você não tenha processado o evento ou ele deva continuar para qualquer outro listener de chave.
  • onTouch(): isso retorna um booleano para indicar se o listener consome esse evento. O importante é que esse evento pode ter várias ações que seguem umas às outras. Portanto, se retornar false quando o evento de ação inferior for recebido, você indicará que não consumiu o evento e que não está interessado em ações subsequentes desse evento. Logo, você não será chamado para outras ações dentro do evento, como um gesto de dedo ou um evento de ação para cima eventual.

Lembre-se de que os eventos de tecla de hardware sempre são entregues à View atualmente em foco. Eles são enviados a partir da parte superior da hierarquia da View e seguem para baixo até atingir o destino adequado. Se a View (ou um filho da View) estiver em foco, é possível ver o percurso do evento pelo método dispatchKeyEvent(). Como alternativa para capturar eventos de tecla na View, também é possível receber todos os eventos dentro da Activity usando onKeyDown() e onKeyUp().

Além disso, ao pensar sobre a entrada de texto para o aplicativo, lembre-se de que vários dispositivos têm somente métodos de entrada de software. Esses métodos não precisam ser baseados em teclas, alguns podem usar entrada de texto por voz, por escrita e outros. Mesmo se um método de entrada apresentar uma interface parecida com teclado, geralmente ele não ativa a família de eventos onKeyDown(). Nunca crie uma IU que exija pressionamentos de teclas específicas para ser controlada, a não ser que você queira limitar o aplicativo a dispositivos com um teclado de hardware. Em particular, não confie nesses métodos para validar a entrada quando o usuário pressiona a tecla de retorno. Em vez disso, use ações como IME_ACTION_DONE para sinalizar ao método de entrada como o aplicativo espera reagir para que ele possa alterar a IU de forma significativa. Evite suposições sobre como um método de entrada de software funcionará e confie somente no fornecimento do texto já formatado para o aplicativo.

Observação: o Android chamará gerenciadores de evento e, em seguida, gerenciadores adequados padrão da definição de classe. Logo, retornar true desses listeners de evento interromperá a propagação do evento para outros listeners de evento e também bloqueará o callback para o gerenciador de evento padrão na View. Portanto, verifique se quer encerrar o evento ao retornar true.

Gerenciadores de evento

Se estiver criando um componente personalizado a partir de View, você poderá definir vários métodos de callback usados como gerenciadores de evento padrão. No documento sobre Componentes personalizados da View, você aprenderá a ver alguns callbacks usados para lidar com eventos, inclusive:

Há alguns outros métodos de que você deve ter ciência que não fazem parte da classe View, mas podem ter impacto direto na maneira de lidar com os eventos. Portanto, ao gerenciar eventos mais complexos dentro de um layout, considere esses outros métodos:

Modo de toque

Quando um usuário está navegando em uma interface do usuário com teclas direcionais ou trackball, é necessário fornecer foco para itens de ação (como botões) para que o usuário possa ver o que aceitará entrada. Se o dispositivo tiver capacidades de toque, no entanto, e o usuário começar a interagir com a interface por meio de toque, não será mais necessário destacar itens nem fornecer foco para uma View específica. Contudo, há um modo de interação chamado “modo de toque”.

Para dispositivos com capacidades de toque, quando o usuário toca na tela, o dispositivo entra no modo de toque. A partir desse ponto, somente as Views que tiverem isFocusableInTouchMode() como verdadeiro poderão ter foco, como widgets de edição de texto. Outras Views tocáveis, como botões, não receberão foco ao serem tocadas. Em vez disso, elas simplesmente dispararão listeners de clique quando forem pressionadas.

Sempre que um usuário pressionar teclas direcionais ou rolar com o trackball, o dispositivo sairá do modo de toque e encontrará uma visualização para atribuir foco. Agora, o usuário pode retomar a interação com a interface do usuário sem tocar na tela.

O estado de modo de toque é mantido em todo o sistema (em todas as janelas e atividades). Para consultar o estado atual, é possível chamar isInTouchMode() para ver se o dispositivo está no modo de toque no momento.

Processamento de foco

A biblioteca lidará com a rotina de movimento de foco em resposta à entrada do usuário. Isso inclui a mudança de foco à medida que as Views são removidas ou ocultadas, ou à medida que novas Views se tornem disponíveis. As Views indicam a prontidão para receber foco por meio do método isFocusable(). Para determinar se uma View pode receber foco, chame setFocusable(). Quando no modo de toque, é possível consultar se uma View permite foco com isFocusableInTouchMode(). Isso pode ser alterado com setFocusableInTouchMode().

Em dispositivos que executam o Android 9 (API de nível 28) ou superior, as atividades não atribuem um foco inicial. Em vez disso, você precisa solicitar explicitamente o foco inicial, caso queira.

O movimento de foco é baseado em um algoritmo que encontra um semelhante mais próximo em uma dada direção. Em casos raros, o algoritmo padrão pode não corresponder ao comportamento pretendido do desenvolvedor. Nessas situações, é possível fornecer modificações explícitas com os seguintes atributos XML no arquivo do layout: nextFocusDown, nextFocusLeft, nextFocusRight e nextFocusUp. Adicione um desses atributos à View a partir do foco que ela está abandonando. Defina o valor do atributo para ser o código da View para o foco que deve ser fornecido. Por exemplo:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

Geralmente, nesse layout vertical, navegar do primeiro botão para cima não resultaria em nada, nem navegar do segundo botão para baixo. Agora que o botão superior definiu o botão inferior como nextFocusUp (e vice-versa), o foco da navegação mudará de cima para baixo e de baixo para cima.

Caso queira declarar uma View como alvo de foco na IU (quando tradicionalmente não é), adicione o atributo XML android:focusable à View na declaração do layout. Defina o valor como true. Também é possível declarar uma View como alvo de foco no modo de toque com android:focusableInTouchMode.

Para solicitar foco a uma determinada View, chame requestFocus().

Para ouvir eventos de foco (receber notificações quando uma View receber ou perder foco), use onFocusChange(), como discutido na seção Listeners de evento acima.