Visão geral dos eventos de entrada

Testar o Compose
O Jetpack Compose é o kit de ferramentas de interface recomendado para Android. Aprenda a usar o toque e a entrada no Compose.

No Android, há mais de uma maneira de interceptar os eventos da interação de um usuário com o app. 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 pelo framework do Android quando a ação em questão ocorre nesse objeto. Por exemplo, quando uma View (como um botão) é tocada, o método onTouchEvent() é chamado neste objeto. No entanto, para interceptar esse evento, você precisa estender a classe e substituir o método. Além disso, estender todos os objetos 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 callbacks que podem ser definidas com muito mais facilidade. Essas interfaces, chamadas de listeners de eventos, são a forma de capturar a interação do usuário com a IU.

Geralmente, os listeners de eventos são usados para detectar a interação do usuário. No entanto, pode haver casos em que você queira 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 eventos padrão para a classe usando manipuladores de eventos.

Listeners de eventos

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

Os seguintes métodos de callback estão incluídos nas interfaces do listener de eventos:

onClick()
De View.OnClickListener. É chamado quando o usuário toca no item (no modo de toque) ou foca no item com as teclas de navegação ou o trackball e pressiona a tecla "Enter" adequada ou pressiona o trackball.
onLongClick()
De View.OnLongClickListener. É chamado quando o usuário mantém o item pressionado (no modo de toque) ou foca no item com as teclas de navegação ou o trackball e mantém pressionada a tecla "Enter" adequada ou mantém o trackball pressionado (por um segundo).
onFocusChange()
De View.OnFocusChangeListener. É chamado quando o usuário navega para ou do item usando as teclas de navegação ou o trackball.
onKey()
De View.OnKeyListener. É chamado quando o usuário está com foco no item e pressiona ou solta uma tecla de hardware no dispositivo.
onTouch()
De View.OnTouchListener. É chamado quando o usuário realiza uma ação qualificada como um evento de toque, incluindo o pressionamento, a liberação ou qualquer outro gesto de movimento na tela (dentro dos limites do item).
onCreateContextMenu()
De View.OnCreateContextMenuListener. É chamado quando um menu de contexto está sendo criado (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 das respectivas interfaces. Para definir um desses métodos e lidar com seus eventos, implemente a interface aninhada na atividade ou defina-a como uma classe anônima. Em seguida, transmita uma instância da sua implementação para o respectivo método View.set...Listener(). Por exemplo: chame setOnClickListener() e transmita-o à implementação de OnClickListener.

O exemplo abaixo mostra como registrar um listener de cliques 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 de objetos. 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 eventos podem retornar um booleano. O motivo depende do evento. Este é o motivo para os poucos que retornam:

  • onLongClick(): retorna um booleano para indicar se você consumiu o evento e se ele não pode 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 precise continuar para qualquer outro listener de cliques.
  • onKey(): retorna um booleano para indicar se você consumiu o evento e se ele não pode ser levado adiante. Ou seja, retorne verdadeiro para indicar que você processou o evento e ele não precisa seguir adiante, ou retorne falso caso você não tenha processado o evento ou ele precise continuar para qualquer outro listener de chaves.
  • onTouch(): 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 falso 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. Portanto, 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 de visualização 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 com o método dispatchKeyEvent(). Como alternativa para capturar eventos de tecla na View, também é possível receber todos os eventos dentro da atividade usando onKeyDown() e onKeyUp().

Além disso, ao pensar sobre a entrada de texto para o app, 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 app 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 app 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 app.

Observação: o Android chamará manipuladores de eventos e, em seguida, manipuladores padrão apropriados para a definição de classe. Portanto, retornar verdadeiro desses listeners de eventos interromperá a propagação do evento para outros listeners de eventos e também bloqueará o callback para o manipulador de eventos padrão na View. Portanto, verifique se quer encerrar o evento ao retornar verdadeiro.

Manipuladores de eventos

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

Há alguns outros métodos que você precisa conhecer 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 estes 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. No entanto, se o dispositivo tiver recursos de toque, 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. Assim, há um modo de interação chamado "modo de toque".

Para dispositivos com recursos de toque, quando o usuário tocar na tela, o dispositivo entrará no modo de toque. A partir desse ponto, somente as Views que tiverem isFocusableInTouchMode() definido como "verdadeiro" serão focalizáveis, 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 cliques 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.

Como processar o foco

A biblioteca processará a rotina de movimento de foco em resposta à entrada do usuário. Isso inclui mudar o 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 com o método isFocusable(). Para determinar se uma View pode receber foco, chame setFocusable(). No modo de toque, é possível consultar se uma View permite foco com isFocusableInTouchMode(). Isso pode ser alterado com setFocusableInTouchMode().

Em dispositivos com o Android 9 (API de nível 28) ou mais recente, 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 determinada direção. Em casos raros, o algoritmo padrão pode não corresponder ao comportamento pretendido do desenvolvedor. Nessas situações, é possível fornecer substituições explícitas com os seguintes atributos XML no arquivo de 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 precisa ser fornecido. 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 true. Também é possível declarar uma View como focalizável no modo de toque com android:focusableInTouchMode.

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

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