Android: ¿Diferencia entre onInterceptTouchEvent y dispatchTouchEvent?

¿Cuál es la diferencia entre onInterceptTouchEvent y dispatchTouchEvent en Android?

De acuerdo con la guía del desarrollador android, ambos métodos pueden ser utilizados para interceptar un evento táctil ( MotionEvent ), pero ¿cuál es la diferencia?

¿Cómo onInterceptTouchEvent , dispatchTouchEvent y onTouchEvent en una jerarquía de Views ( ViewGroup )?

El mejor lugar para desmitificar esto es el código fuente. Los docs son lamentablemente inadecuados para explicar esto.

DispatchTouchEvent realmente se define en Activity, View y ViewGroup. Piense en él como un regulador que decida cómo dirigir los acontecimientos del tacto.

Por ejemplo, el caso más simple es el de View.dispatchTouchEvent que enrutará el evento táctil a OnTouchListener.onTouch si está definido o al método de extensión onTouchEvent .

Para ViewGroup.dispatchTouchEvent las cosas son mucho más complicadas. Necesita averiguar cuál de sus vistas secundarias debe obtener el evento (llamando a child.dispatchTouchEvent). Esto es básicamente un algoritmo de prueba de éxito en el que averiguar qué rectángulo delimitador de la vista secundaria contiene las coordenadas del punto de contacto.

Pero antes de que pueda enviar el evento a la vista apropiada del niño, el padre puede espiar y / o interceptar el evento todos juntos. Esto es para lo que onInterceptTouchEvent existe. Así que llama a este método primero antes de hacer la prueba de éxito y si el evento fue secuestrado (devolviendo true de onInterceptTouchEvent) envía un ACTION_CANCEL a las vistas secundarias para que puedan abandonar su procesamiento de eventos táctiles (desde eventos táctiles anteriores) ya partir de entonces Todos los eventos de toque en el nivel padre se envían a onTouchListener.onTouch (si está definido) o onTouchEvent (). También en ese caso, onInterceptTouchEvent nunca se llama de nuevo.

¿Desea incluso anular [Activity | ViewGroup | View] .dispatchTouchEvent? A menos que esté haciendo algún enrutamiento personalizado, probablemente no debería hacerlo.

Los métodos principales de extensión son ViewGroup.onInterceptTouchEvent si desea espiar y / o interceptar evento táctil en el nivel padre y View.onTouchListener / View.onTouchEvent para el manejo de eventos principales.

En general, su diseño demasiado complicado, pero los apis androides se inclinan más hacia la flexibilidad que la simplicidad.

Porque este es el primer resultado en Google. Quiero compartir con ustedes una gran charla de Dave Smith en Youtube: dominar el sistema de toque de Android y las diapositivas están disponibles aquí . Me dio una buena comprensión profunda sobre el sistema del tacto de Androide:

Cómo se maneja la actividad :

  • Activity.dispatchTouchEvent()
    • Siempre primero para ser llamado
    • Envía evento a la vista raíz adjunta a la ventana
    • onTouchEvent()
      • Se llama si no hay vistas que consumen el evento
      • Siempre duran para ser llamado

Cómo se maneja la vista :

  • View.dispatchTouchEvent()
    • Envía el evento al oyente primero, si existe
      • View.OnTouchListener.onTouch()
    • Si no se consume, procesa el toque en sí mismo
      • View.onTouchEvent()

Cómo un grupo de visualización maneja el tacto:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • Compruebe si debe reemplazar a los niños
      • Pasa ACTION_CANCEL al niño activo
      • Devuelve true una vez, consume todos los eventos posteriores
    • Para cada vista de niño, en orden inverso se agregaron
      • Si el tacto es relevante (vista interior), child.dispatchTouchEvent()
      • Si no se maneja por anterior, envíe a la siguiente vista
    • Si ningún niño maneja el evento, el oyente tiene una oportunidad
      • OnTouchListener.onTouch()
    • Si no escucha, o no se maneja
      • onTouchEvent()
  • Los eventos interceptados saltan sobre el paso del niño

También proporciona código de ejemplo de toque personalizado en github.com/devunwired/ .

Respuesta: Básicamente se dispatchTouchEvent() en cada capa View para determinar si una View está interesada en un gesto en curso. En un ViewGroup el ViewGroup tiene la capacidad de robar los eventos táctiles en su método dispatchTouchEvent() , antes de llamar a dispatchTouchEvent() en los niños. El ViewGroup sólo detendría el envío si el ViewGroup onInterceptTouchEvent() -method devuelve true. La diferencia es que dispatchTouchEvent() está enviando MotionEvents y onInterceptTouchEvent indica si debe interceptar (no enviar el MotionEvent a los niños) o no (despachar a los niños) .

Podrías imaginar el código de un ViewGroup haciendo más o menos esto (muy simplificado):

 public boolean dispatchTouchEvent(MotionEvent ev) { if(!onInterceptTouchEvent()){ for(View child : children){ if(child.dispatchTouchEvent(ev)) return true; } } return super.dispatchTouchEvent(ev); } 

Hay mucha confusión acerca de estos métodos, pero en realidad no es tan complicado. La mayor parte de la confusión es porque:

  1. Si su vista (grupo) o cualquiera de sus hijos no devuelven true en onTouchEvent, dispatchTouchEvent y onInterceptTouchEvent SOLAMENTE se llamará para MotionEvent.ACTION_DOWN. Sin un verdadero de onTouchEvent, la vista principal asumirá que su vista no necesita los MotionEvents.
  2. Cuando ninguno de los hijos de un ViewGroup devuelve true en onTouchEvent, onInterceptTouchEvent SOLAMENTE se llamará para MotionEvent.ACTION_DOWN, incluso si su ViewGroup devuelve true en onTouchEvent.

La orden de procesamiento es la siguiente:

  1. DispatchTouchEvent se llama.
  2. OnInterceptTouchEvent se llama a MotionEvent.ACTION_DOWN o cuando alguno de los hijos de ViewGroup devuelve true en onTouchEvent.
  3. OnTouchEvent se llama por primera vez a los hijos de ViewGroup y cuando ninguno de los niños devuelve true se llama en la vista (Group).

Si desea obtener una vista previa de TouchEvents / MotionEvents sin deshabilitar los eventos de sus hijos, debe hacer dos cosas:

  1. Anular dispatchTouchEvent para obtener una vista previa del evento y devolver super.dispatchTouchEvent (ev);
  2. Substituya onTouchEvent y devuelva true, de lo contrario no obtendrá ningún MotionEvent excepto MotionEvent.ACTION_DOWN.

Si desea detectar algún gesto como un evento de deslizar, sin deshabilitar otros eventos en sus hijos, siempre y cuando no haya detectado el gesto, puede hacerlo así:

  1. Visualice los MotionEvents como se describió anteriormente y establezca un indicador cuando detecte su gesto.
  2. Devuelve true en onInterceptTouchEvent cuando tu bandera está configurada para cancelar el procesamiento de MotionEvent por tus hijos. Este es también un lugar conveniente para restablecer su bandera, porque onInterceptTouchEvent no será llamado de nuevo hasta el próximo MotionEvent.ACTION_DOWN.

Ejemplo de reemplazos en un FrameLayout (mi ejemplo en C # es como estoy programando con Xamarin Android, pero la lógica es la misma en Java):

 public override bool DispatchTouchEvent(MotionEvent e) { // Preview the touch event to detect a swipe: switch (e.ActionMasked) { case MotionEventActions.Down: _processingSwipe = false; _touchStartPosition = e.RawX; break; case MotionEventActions.Move: if (!_processingSwipe) { float move = e.RawX - _touchStartPosition; if (move >= _swipeSize) { _processingSwipe = true; _cancelChildren = true; ProcessSwipe(); } } break; } return base.DispatchTouchEvent(e); } public override bool OnTouchEvent(MotionEvent e) { // To make sure to receive touch events, tell parent we are handling them: return true; } public override bool OnInterceptTouchEvent(MotionEvent e) { // Cancel all children when processing a swipe: if (_cancelChildren) { // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down: _cancelChildren = false; return true; } return false; } 

Me encontré con una explicación muy intuitiva en esta página web http://doandroids.com/blogs/tag/codeexample/ . Tomado de allí:

  • Boolean onTouchEvent (MotionEvent ev): se llama cuando se detecta un evento táctil con esta vista como objetivo
  • Boolean onInterceptTouchEvent (MotionEvent ev): se llama cuando se detecta un evento táctil con este ViewGroup o un hijo de él como destino. Si esta función devuelve true, el objeto MotionEvent será interceptado, lo que significa que no se pasará al niño, sino al onTouchEvent de esta vista.

DispatchTouchEvent maneja antes onInterceptTouchEvent.

Usando este ejemplo simple:

  main = new LinearLayout(this){ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { System.out.println("Event - onInterceptTouchEvent"); return super.onInterceptTouchEvent(ev); //return false; //event get propagated } @Override public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("Event - dispatchTouchEvent"); return super.dispatchTouchEvent(ev); //return false; //event DONT get propagated } }; main.setBackgroundColor(Color.GRAY); main.setLayoutParams(new LinearLayout.LayoutParams(320,480)); viewA = new EditText(this); viewA.setBackgroundColor(Color.YELLOW); viewA.setTextColor(Color.BLACK); viewA.setTextSize(16); viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80)); main.addView(viewA); setContentView(main); 

Puedes ver que el registro será como:

 I/System.out(25900): Event - dispatchTouchEvent I/System.out(25900): Event - onInterceptTouchEvent 

Así que en caso de que usted está trabajando con estos 2 manejadores use dispatchTouchEvent para manejar en primera instancia el evento, que irá a onInterceptTouchEvent.

Otra diferencia es que si dispatchTouchEvent devuelve 'false' el evento no se propaga al niño, en este caso el EditText, mientras que si devuelve false en onInterceptTouchEvent el evento sigue recibiendo el envío a EditText

Puedes encontrar la respuesta en este video https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19 y los próximos 3 videos. Todos los eventos táctiles se explican muy bien, es muy claro y lleno de ejemplos.

El siguiente código dentro de una subclase ViewGroup evitaría que los contenedores primarios reciban eventos táctiles:

  @Override public boolean dispatchTouchEvent(MotionEvent ev) { // Normal event dispatch to this container's children, ignore the return value super.dispatchTouchEvent(ev); // Always consume the event so it is not dispatched further up the chain return true; } 

Utilicé esto con una superposición personalizada para evitar que las vistas de fondo respondieran a los eventos de toque.

La principal diferencia :

• Activity.dispatchTouchEvent (MotionEvent): esto permite a su actividad interceptar todos los eventos táctiles antes de que se envíen a la ventana.
• ViewGroup.onInterceptTouchEvent (MotionEvent): permite a un grupo de vistas ver los eventos a medida que se envían a vistas secundarias.

El objeto onInterceptTouchEvent() de onInterceptTouchEvent() es siempre el punto de entrada para el evento ACTION_DOWN que es el primer evento que se produce.

Si desea que ViewGroup procese este gesto, devuelva true de onInterceptTouchEvent() . Al devolver true, onTouchEvent onTouchEvent() de onTouchEvent() recibirá todos los eventos siguientes hasta ACTION_UP o ACTION_CANCEL , y en la mayoría de los casos, los eventos táctiles entre ACTION_DOWN y ACTION_UP o ACTION_CANCEL son ACTION_MOVE , que normalmente se reconocerán como gestos de desplazamiento / fling.

Si devuelve false de onInterceptTouchEvent() , se onInterceptTouchEvent() la vista de onTouchEvent() . Se repetirá para los mensajes siguientes hasta que devuelva true de onInterceptTouchEvent() .

Fuente: http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html

Tanto Activity como View tienen el método dispatchTouchEvent () y onTouchEvent. El ViewGroup también tiene estos métodos, pero tiene otro método llamado onInterceptTouchEvent. El tipo de retorno de esos métodos es booleano, puede controlar la ruta de envío a través del valor devuelto.

El envío de eventos en Android comienza desde Activity-> ViewGroup-> View.

Pequeña respuesta:

OnInterceptTouchEvent viene antes de setOnTouchListener.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.