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
e transmita-o à implementação de setOnClickListener()
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:
: 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.onLongClick()
: 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.onKey()
: 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.onTouch()
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
. Como alternativa para capturar eventos de tecla na View, também é possível receber
todos os eventos dentro da atividade usando dispatchKeyEvent()
e onKeyDown()
.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
. 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 onKeyDown()
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:
: chamado quando ocorre um novo evento de tecla.onKeyDown(int, KeyEvent)
: chamado quando ocorre um evento de liberação de tecla.onKeyUp(int, KeyEvent)
: chamado quando ocorre um evento de movimento do trackball.onTrackballEvent(MotionEvent)
: chamado quando ocorre um evento de movimento da tela touchscreen.onTouchEvent(MotionEvent)
: chamado quando a visualização ganha ou perde foco.onFocusChanged(boolean, int, Rect)
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:
: permite queActivity.dispatchTouchEvent(MotionEvent)
Activity
intercepte todos os eventos de toque antes de serem enviados à janela.
: permite queViewGroup.onInterceptTouchEvent(MotionEvent)
ViewGroup
observe os eventos à medida que são enviados para as Views filhas.
: faça essa chamada em uma View pai para indicar que ela não precisa interceptar eventos de toque comViewParent.requestDisallowInterceptTouchEvent(boolean)
.onInterceptTouchEvent(MotionEvent)
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
. Para determinar se uma View pode receber
foco, chame isFocusable()
. No modo de toque,
é possível consultar se uma View permite foco com setFocusable()
.
Isso pode ser alterado com isFocusableInTouchMode()
.
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
,
conforme discutido na seção Listeners de eventos acima.onFocusChange()