Descripción general de los eventos de entrada

Prueba el estilo de Compose
Jetpack Compose es el kit de herramientas de IU recomendado para Android. Aprende a usar el control táctil y la entrada en Compose.

En Android, existe más de una forma de interceptar los eventos desde una interacción del usuario con tu aplicación. Al considerar los eventos dentro de tu interfaz de usuario, el enfoque consiste en capturar los eventos desde el objeto de vista específico con el que interactúa el usuario. La clase View proporciona los medios para hacerlo.

Dentro de las diversas clases View que utilizarás para componer tu diseño, es posible que notes varios métodos de devolución de llamada públicos que puedan ser útiles para eventos de IU. El framework de Android llama a estos métodos cuando la acción respectiva ocurre en ese objeto. Por ejemplo, cuando se toca una vista (como un botón), se llama al método onTouchEvent() en ese objeto. Sin embargo, para interceptar esto, debes extender la clase y anular el método. No obstante, extender todos los objetos View para controlar ese tipo de evento no sería práctico. Por eso, la clase View también contiene una colección de interfaces anidadas con devoluciones de llamada que puedes definir más fácilmente. Estas interfaces, llamadas objetos de escucha de eventos, te permiten capturar la interacción del usuario con tu IU.

Si bien generalmente utilizarás los objetos de escucha de eventos para detectar la interacción del usuario, quizás en algún momento desees extender una clase View a fin de crear un componente personalizado. Quizás desees extender la clase Button para crear algo más elaborado. En este caso, podrás definir los comportamientos de eventos predeterminados para tu clase utilizando los controladores de eventos de la clase.

Objetos de escucha de eventos

Un objeto de escucha de eventos es una interfaz de la clase View que contiene un solo método de devolución de llamada. El framework de Android llamará a estos métodos cuando la vista con la que se haya registrado el objeto de escucha se active por la interacción del usuario con el elemento de la IU.

En las interfaces de los objetos de escucha de eventos, se incluyen los siguientes métodos de devolución de llamada:

onClick()
De View.OnClickListener. Se llama a este método cuando el usuario toca el elemento (en el modo táctil) o se centra en el elemento con las teclas de navegación o la bola de seguimiento y presiona la tecla "Intro" adecuada o la bola de seguimiento.
onLongClick()
De View.OnLongClickListener. Se llama a este método cuando el usuario mantiene presionado el elemento (en el modo táctil) o se centra en el elemento con las teclas de navegación o la bola de seguimiento y mantiene presionada la tecla "Intro" adecuada o la bola de seguimiento (durante un segundo).
onFocusChange()
De View.OnFocusChangeListener. Se llama a este método cuando el usuario navega hacia el elemento o sale de él utilizando las teclas de navegación o la bola de seguimiento.
onKey()
De View.OnKeyListener. Se llama a este método cuando el usuario se centra en el elemento y presiona o suelta una tecla de hardware del dispositivo.
onTouch()
De View.OnTouchListener. Se llama a este método cuando el usuario realiza una acción calificada como evento táctil, por ejemplo, presionar, soltar o cualquier gesto de movimiento en la pantalla (dentro de los límites del elemento).
onCreateContextMenu()
De View.OnCreateContextMenuListener. Se llama a este método cuando se crea un menú contextual (como resultado de un "clic largo" sostenido). Consulta la explicación sobre menús contextuales en la guía para desarrolladores Menús.

Estos métodos son los únicos habitantes de su respectiva interfaz. Para definir uno de estos métodos y controlar tus eventos, implementa la interfaz anidada en tu actividad o defínela como una clase anónima. Luego, pasa una instancia de tu implementación al método View.set...Listener() respectivo (p. ej., llama a setOnClickListener() y pásale tu implementación del objeto OnClickListener).

En el ejemplo siguiente, se muestra cómo registrar un receptor onClick para un botón.

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);
    ...
}

Quizás también te parezca más conveniente implementar OnClickListener como parte de tu actividad. Esto evitará la carga extra de la clase y la asignación de objetos. Por ejemplo:

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
    }
    ...
}

Ten en cuenta que la devolución de llamada onClick() del ejemplo anterior no tiene un valor para mostrar, pero algunos otros métodos de objeto de escucha de eventos deben mostrar un valor booleano. El motivo depende del evento. A continuación, se explican los motivos de los pocos casos que lo hacen:

  • onLongClick(): Este método muestra un valor booleano para que indiques si procesaste el evento y este no debe continuar. Es decir, muestra un valor verdadero para indicar que usaste el evento y que debe detenerse aquí, o bien muestra un valor falso si no procesaste el evento o si este debe continuar en otros objetos de escucha de clic.
  • onKey(): Este método muestra un valor booleano para que indiques si procesaste el evento y este no debe continuar. Es decir, muestra un valor verdadero para indicar que usaste el evento y que debe detenerse aquí, o bien muestra falso si no procesaste el evento o si este debe continuar en otros objetos de escucha de tecla.
  • onTouch(): Este método muestra un valor booleano para que indiques si tu objeto de escucha procesa este evento. Lo importante es que este evento puede tener varias acciones consecutivas. Por lo tanto, si muestras falso cuando se reciba el evento de acción de abajo, estarás indicando que no usaste el evento ni te interesan las acciones subsiguientes de este evento. Por ende, no se te llamará para otras acciones dentro del evento, como un gesto táctil, o el evento de acción de arriba final.

Recuerda que los eventos de teclas de hardware siempre se entregan a la vista actualmente en foco. Se distribuyen comenzando desde la parte superior de la jerarquía de vistas y luego hacia abajo, hasta llegar al destino correspondiente. Si tu vista (o un elemento secundario de ella) está en foco, puedes ver cómo se traslada el evento a través del método dispatchKeyEvent(). Como alternativa a capturar eventos de teclas mediante tu vista, también puedes recibir todos los eventos dentro de tu actividad con onKeyDown() y onKeyUp().

Además, al considerar la entrada de texto para tu aplicación, recuerda que muchos dispositivos solo tienen métodos de entrada de software. No es necesario que esos métodos se basen en teclas; algunos pueden utilizar entrada de voz, escritura a mano, etc. Incluso si un método de entrada presenta una interfaz similar a un teclado, generalmente no activará la familia de eventos onKeyDown(). Nunca debes compilar una IU que requiera que se presionen teclas específicas para controlarla, salvo que desees limitar tu aplicación a dispositivos con un teclado de hardware. En particular, no utilices estos métodos para validar la entrada cuando el usuario presione la tecla para regresar; en cambio, utiliza acciones como IME_ACTION_DONE para señalar al método de entrada cómo espera reaccionar tu aplicación a fin de que pueda cambiar su IU de forma significativa. Evita los supuestos sobre la forma en la que debe funcionar un método de entrada de software y simplemente confía en él para proporcionar texto ya formateado a tu aplicación.

Nota: Android llamará en primer lugar a los controladores de eventos y en segundo lugar a los controladores predeterminados correspondientes de la definición de clase. Por lo tanto, al mostrar verdadero desde estos objetos de escucha de eventos, se detendrá la propagación del evento a otros objetos de escucha de eventos y también se bloqueará la devolución de llamada al controlador de eventos predeterminado en la vista. Por eso, asegúrate de que desees finalizar el evento cuando muestres el valor verdadero.

Controladores de eventos

Si estás creando un componente personalizado desde un elemento View, podrás definir varios métodos de devolución de llamada utilizados como controladores de eventos predeterminados. En el documento Componentes de vistas personalizadas, aprenderás sobre algunas de las devoluciones de llamada que se utilizan comúnmente para controlar eventos, incluidas las siguientes:

Existen otros métodos que debes conocer, los cuales no forman parte de la clase de vista, pero pueden afectar directamente la forma en la que controlas eventos. Por ende, al administrar eventos más complejos dentro de un diseño, considera estos otros métodos:

Modo táctil

Cuando un usuario navega en una interfaz de usuario con teclas direccionales o una bola de seguimiento, es necesario enfocar los elementos que se pueden accionar (como los botones) para que el usuario pueda ver lo que aceptará una entrada. Sin embargo, si el dispositivo tiene funciones táctiles y el usuario comienza a interactuar con la interfaz tocándola, ya no es necesario destacar los elementos ni enfocar una vista determinada. Por lo tanto, hay un modo de interacción denominado "modo táctil".

En el caso de un dispositivo con funciones táctiles, una vez que el usuario toca la pantalla, el dispositivo entra en el modo táctil. A partir de este momento, solo serán enfocables los objetos de vista para los que isFocusableInTouchMode() es verdadero (por ejemplo, los widgets de edición de texto). Otras vistas que son táctiles, como los botones, no se enfocarán cuando el usuario las toque, simplemente activarán sus objetos de escucha de clic cuando el usuario las presione.

Cuando un usuario toque una tecla direccional o se desplace con una bola de seguimiento, el dispositivo saldrá del modo táctil y encontrará una vista para enfocar. Ahora, el usuario puede reanudar la interacción con la interfaz de usuario sin tocar la pantalla.

El estado del modo táctil se mantiene en todo el sistema (todas las ventanas y actividades). A fin de consultar el estado actual, puedes llamar a isInTouchMode() para comprobar si el dispositivo está en el modo táctil.

Cómo controlar el foco

El framework controlará el movimiento del foco de rutina en respuesta a las entradas del usuario. Esto incluye cambiar el foco a medida que se oculten o quiten las vistas, o a medida que haya nuevas vistas disponibles. Las vistas indican su disposición a permanecer enfocadas a través del método isFocusable(). Para cambiar si una vista puede permanecer enfocada o no, llama a setFocusable(). En el modo táctil, puedes consultar si una vista permite el enfoque con isFocusableInTouchMode(). Puedes cambiar esto con setFocusableInTouchMode().

En dispositivos que ejecutan Android 9 (nivel de API 28) o una versión posterior, las actividades no asignan un foco inicial. En cambio, debes solicitar un foco inicial de forma explícita si lo deseas.

El movimiento del foco se basa en un algoritmo que encuentra el vecino más cercano en una dirección determinada. En pocos casos, es posible que el algoritmo predeterminado no coincida con el comportamiento previsto del desarrollador. En estas situaciones, puedes proporcionar anulaciones explícitas con los siguientes atributos XML en el archivo de diseño: nextFocusDown, nextFocusLeft, nextFocusRight y nextFocusUp. Agrega uno de estos atributos a la vista desde la que se quita el foco. Define el valor del atributo que será el ID de la vista en la que se pondrá el foco. Por ejemplo:

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

Generalmente, en este diseño vertical, al navegar hacia arriba desde el primer botón, no se llegaría a ningún lado, y lo mismo ocurriría si se navegara hacia abajo desde el segundo botón. Ahora que el botón superior definió el inferior como nextFocusUp (y viceversa), el foco de navegación pasará desde arriba hacia abajo y desde abajo hacia arriba.

Si deseas declarar una vista como enfocable en tu IU (cuando tradicionalmente no lo es), agrega el atributo XML android:focusable a la vista en tu declaración de diseño. Establece el valor true. También puedes declarar una vista como enfocable en el modo táctil con android:focusableInTouchMode.

Para solicitar que se enfoque un elemento View en particular, llama a requestFocus().

Para detectar eventos de foco (ser notificado cuando un elemento View recibe o pierde el foco), utiliza onFocusChange(), como se explica en la sección Objetos de escucha de eventos.