¿Cómo se entregan los eventos táctiles de Android?

No estoy preguntando cómo manejar eventos táctiles, pero ¿qué está sucediendo detrás de escena? Si hay varios widgets nesteds, ¿en qué orden ven los eventos? ¿El desarrollador tiene algún control sobre eso? Idealmente, me gustaría un documento sobre el tema.

Desde el punto de vista de la actividad:

Los eventos táctiles se entregan primero a Activity.dispatchTouchEvent. Es donde puedes atraparlos primero.

Aquí se envían a la ventana, donde atraviesan la jerarquía de la vista, en ese orden que los widgets que se dibujan al final (sobre otros widgets) tienen la oportunidad de procesar el tacto en View.onTouchEvent primero. Si alguna Vista devuelve verdadero en onTouchEvent, el recorrido se detiene y otras Vistas no reciben evento táctil.

Finalmente, si ninguna Vista consume el toque, se entrega a Activity.onTouchEvent.

Eso es todo tu control. Y es lógico que lo que ves dibujado sobre otra cosa, tenga la oportunidad de procesar el evento táctil antes que algo dibujado debajo de él.

Echemos un vistazo a un ejemplo visual.

enter image description here

Cuando ocurre un evento táctil, primero todos son notificados del evento, comenzando en la Actividad e yendo hasta la vista en la parte superior. Luego, todos tienen la oportunidad de manejar el evento, comenzando con la vista en la parte superior y volviendo a la actividad. Entonces, la Actividad es la primera en enterarse y la última en tener la oportunidad de manejarlo.

enter image description here

Si algún grupo de vistas quiere manejar el evento táctil de inmediato (y no le da a alguien más la oportunidad de hacerlo), entonces puede devolver la true en su onInterceptTouchEvent() . Una actividad no tiene onInterceptTouchEvent() pero puede anular dispatchTouchEvent() para hacer lo mismo.

Si una Vista (o un Grupo de Vistas) tiene un OnTouchListener , el evento táctil es manejado por OnTouchListener.onTouch() . De lo contrario, es manejado por onTouchEvent() . Si onTouchEvent() devuelve true para cualquier evento táctil, entonces el manejo se detiene allí. Nadie más en el futuro tiene la oportunidad de hacerlo.

Explicación más detallada

El diagtwig de arriba hace las cosas un poco más simples de lo que realmente son. Por ejemplo, entre la actividad y el grupo de visualización A (el diseño raíz) también están la ventana y el decorado. Los dejé arriba porque generalmente no tenemos que interactuar con ellos. Sin embargo, los incluiré a continuación. La siguiente descripción sigue un evento táctil a través del código fuente. Puede hacer clic en un enlace para ver el código fuente real.

(Actualización: el código fuente se ha actualizado, por lo que los números de línea están desactivados ahora, pero al hacer clic en los enlaces, se le guiará al archivo correcto. Haga una búsqueda del nombre del método).

  1. El dispatchTouchEvent() recibe una notificación de un evento táctil. El evento táctil se pasa como un MotionEvent , que contiene las coordenadas x, y, la hora, el tipo de evento y otra información.
  2. El evento táctil se envía a superDispatchTouchEvent() la ventana. Window es una clase abstracta. La implementación real es PhoneWindow .
  3. El siguiente en la fila para recibir la notificación es DecorView’s superDispatchTouchEvent() . DecorView es lo que maneja la barra de estado, la barra de navegación, el área de contenido, etc. En realidad, es solo una subclase de FrameLayout , que a su vez es una subclase de ViewGroup .
  4. El siguiente para recibir la notificación (corríjame si me equivoco) es la vista de contenido de tu actividad. Eso es lo que establece como el diseño raíz de su actividad en xml cuando crea el diseño en el Editor de diseño de Android Studio. Entonces, ya sea que elija un RelativeLayout , un LinearLayout o un ConstraintLayout , todos son subclases de ViewGroup . Y ViewGroup recibe notificaciones del evento táctil en dispatchTouchEvent() . Este es el ViewGroup A en mis diagtwigs arriba.
  5. ViewGroup notificará a cualquier niño que tenga sobre el evento táctil, incluidos los hijos de ViewGroup . Este es ViewGroup B en mis diagtwigs arriba.
  6. En cualquier parte del camino, un grupo de ViewGroup puede ViewGroup un cortocircuito en el proceso de notificación devolviéndolo true para onInterceptTouchEvent() .
  7. Asumiendo que ningún grupo de ViewGroup corta las notificaciones, el final natural de la línea para las notificaciones es cuando se llama a dispatchTouchEvent() get de la vista.
  8. Ahora es el momento de comenzar a manejar los eventos. Si hay un OnTouchListener , entonces tiene la primera oportunidad de manejar el evento táctil con onTouch() . De lo contrario , el onTouchEvent() View se onTouchEvent() de manejarlo.
  9. Ahora todos los ViewGroups recursivamente arriba de la línea tienen la oportunidad de manejar el evento táctil de la misma manera que lo hizo View . Aunque no indiqué esto en el diagtwig anterior, un ViewGroup es una subclase View , por lo que todo lo que describí sobre OnTouchListener.onTouch() y onTouchEvent() también se aplica a ViewGroups.
  10. Finalmente , si nadie más lo quería, la Actividad también tiene la última oportunidad de manejar el evento con onTouchEvent() .

Preguntas más frecuentes

¿Cuándo necesitaría reemplazar a dispatchTouchEvent() ?

Sáltelo en la Actividad si desea capturar un evento táctil antes de que cualquiera de las vistas tenga la oportunidad de hacerlo. Para un ViewGroup (incluida la vista raíz), simplemente anule onInterceptTouchEvent() y onTouchEvent() .

¿Cuándo tendré que anular onInterceptTouchEvent() ?

Si solo quiere espiar las notificaciones táctiles que están entrando, puede hacerlo aquí y devolver false .

Sin embargo, el objective principal de anular este método es permitir que ViewGroup maneje un cierto tipo de evento táctil mientras permite que el niño maneje otro tipo. Por ejemplo, un ScrollView hace esto para manejar el desplazamiento mientras deja que su hijo maneje algo así como un clic de botón. Por el contrario, si la vista hija no quiere que su padre robe su evento táctil, puede llamar a requestDisallowTouchIntercept() .

¿Cuáles son los tipos de eventos táctiles?

Los principales son

  • ACTION_DOWN – Este es el comienzo de un evento táctil. Siempre debe devolver true para el evento onTouchEvent en onTouchEvent si desea manejar un evento táctil. De lo contrario, no recibirá más eventos entregados a usted.
  • ACTION_MOVE : este evento se dispara continuamente a medida que mueves el dedo por la pantalla.
  • ACTION_UP : este es el último evento de un evento táctil.

Un finalista es ACTION_CANCEL . Se llama a esto si un ViewGroup en el árbol decide interceptar el evento táctil.

Puede ver los otros tipos de MotionEvents aquí . Dado que Android es multitáctil, los eventos también se disparan cuando otros dedos (“punteros”) tocan la pantalla.

Estudio adicional

  • Android onTouchEvent Parte 1 , Parte 2 y Parte 3 (video de YouTube: buen resumen de algunos de los enlaces a continuación)
  • Dominar el sistema Android Touch (video completo del desarrollador de Google)
  • Interfaz de usuario de Android interna: Pipeline of Touch’s Touch Event Handling
  • Administrar eventos táctiles en un ViewGroup (documentos de Android)
  • Eventos de entrada (documentos de Android)
  • Gestos y eventos táctiles

siguiendo la respuesta de Suragch,

pseudocódigo:

  public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false; if (onInterceptTouchEvent(ev) { consume = onTouchEvent(ev); } else { consume = child.dispatchTouchEvent(ev); } return consume; } 

ref: Android 开发 艺术 探索