java - propiedad - onclick android studio
Android: ¿Diferencia entre onInterceptTouchEvent y dispatchTouchEvent? (14)
Respuesta suplementaria
Aquí hay algunos suplementos visuales a las otras respuestas. Mi respuesta completa está here .
El método dispatchTouchEvent()
de un ViewGroup
usa onInterceptTouchEvent()
para elegir si debe controlar inmediatamente el evento táctil (con onTouchEvent()
) o continuar notificando los métodos dispatchTouchEvent()
de sus elementos onTouchEvent()
.
¿Cuál es la diferencia entre onInterceptTouchEvent
y dispatchTouchEvent
en Android?
De acuerdo con la guía del desarrollador de Android, ambos métodos pueden usarse para interceptar un evento táctil ( MotionEvent
), pero ¿cuál es la diferencia?
¿Cómo interactúan entre onInterceptTouchEvent
, dispatchTouchEvent
y onTouchEvent
dentro de una jerarquía de Vistas ( ViewGroup
)?
El mejor lugar para desmitificar esto es el código fuente. Los doctores son lamentablemente inadecuados para explicar esto.
dispatchTouchEvent se define realmente en Activity, View y ViewGroup. Piense en ello como un controlador que decide cómo enrutar los eventos táctiles.
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. Debe averiguar cuál de sus vistas secundarias debería obtener el evento (llamando a child.dispatchTouchEvent). Básicamente, este es un algoritmo de prueba de aciertos en el que se determina 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 secundaria apropiada, el padre puede espiar y / o interceptar el evento todos juntos. Esto es para lo que está onInterceptTouchEvent . Por lo tanto, primero llama a este método antes de realizar la prueba de aciertos y si el evento fue secuestrado (devolviendo true desde onInterceptTouchEvent) envía un ACTION_CANCEL a las vistas secundarias para que puedan abandonar su procesamiento de eventos táctiles (de eventos táctiles anteriores) y de ahí en adelante. todos los eventos táctiles en el nivel primario se envían a onTouchListener.onTouch (si está definido) o onTouchEvent (). También en ese caso, onInterceptTouchEvent nunca se vuelve a llamar.
¿Desearía incluso anular [Activity | ViewGroup | View] .dispatchTouchEvent? A menos que esté haciendo un enrutamiento personalizado, probablemente no debería.
Los principales métodos de extensión son ViewGroup.onInterceptTouchEvent si desea espiar y / o interceptar eventos táctiles en el nivel primario y View.onTouchListener / View.onTouchEvent para el manejo de eventos principales.
En general, su diseño demasiado complicado, pero las API de Android se inclinan más hacia la flexibilidad que la simplicidad.
El objeto onInterceptTouchEvent()
de onInterceptTouchEvent()
es siempre el punto de entrada para el evento ACTION_DOWN
, que es el primer evento en ocurrir.
Si desea que ViewGroup procese este gesto, devuelva true desde onInterceptTouchEvent()
. Al regresar verdadero, onTouchEvent onTouchEvent()
de onTouchEvent()
recibirá todos los eventos subsiguientes hasta el próximo 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 / desplazamiento.
Si devuelve falso desde onInterceptTouchEvent()
, se onInterceptTouchEvent()
a onInterceptTouchEvent()
la vista de onTouchEvent()
. Se repetirá para los mensajes subsiguientes hasta que devuelva verdadero desde onInterceptTouchEvent()
.
El siguiente código dentro de una subclase de ViewGroup evitaría que sus contenedores principales 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;
}
Usé esto con una superposición personalizada para evitar que las vistas de fondo respondan a eventos táctiles.
Hay mucha confusión sobre estos métodos, pero en realidad no es tan complicado. La mayor parte de la confusión es porque:
- Si su
View/ViewGroup
o cualquiera de sus hijos no devuelven verdadero enonTouchEvent
,dispatchTouchEvent
yonInterceptTouchEvent
SOLAMENTE serán llamados paraMotionEvent.ACTION_DOWN
. Sin un true deonTouchEvent
, la vista principal asumirá que su vista no necesita los MotionEvents. - 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 enonTouchEvent
.
El orden de procesamiento es así:
- Se llama
dispatchTouchEvent
. -
onInterceptTouchEvent
se llama paraMotionEvent.ACTION_DOWN
o cuando alguno de los hijos de ViewGroup devuelve true enonTouchEvent
. -
onTouchEvent
se llama primero en los hijos de ViewGroup y cuando ninguno de los hijos devuelve true, se llama enView/ViewGroup
.
Si desea obtener TouchEvents/MotionEvents
vista previa de TouchEvents/MotionEvents
sin desactivar los eventos en sus hijos, debe hacer dos cosas:
- Reemplace
dispatchTouchEvent
para obtener una vista previa del evento y devolversuper.dispatchTouchEvent(ev)
; - Sobrescriba
onTouchEvent
y devuelva verdadero, de lo contrario no obtendrá ningúnMotionEvent
exceptoMotionEvent.ACTION_DOWN
.
Si desea detectar algún gesto como un evento de barrido, sin deshabilitar otros eventos en sus hijos mientras no detecte el gesto, puede hacerlo de la siguiente manera:
- Previsualice los MotionEvents como se describe anteriormente y establezca una marca cuando detecte su gesto.
- Devuelva true en
onInterceptTouchEvent
cuando suonInterceptTouchEvent
esté configurada para cancelar el procesamiento de MotionEvent por parte de sus hijos. Este también es un lugar conveniente para restablecer su marca, ya que no se llamará aMotionEvent.ACTION_DOWN
hasta el siguienteMotionEvent.ACTION_DOWN
.
Ejemplo de anulaciones 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;
}
La principal diferencia :
• Activity.dispatchTouchEvent (MotionEvent): esto permite que su actividad intercepte todos los eventos táctiles antes de que se envíen a la ventana.
• ViewGroup.onInterceptTouchEvent (MotionEvent): esto permite que ViewGroup vea eventos a medida que se envían a vistas secundarias.
Me encontré con una explicación muy intuitiva en esta página web http://doandroids.com/blogs/tag/codeexample/ . Tomado de allí:
- onTouchEvent booleano (MotionEvent ev): se llama cuando se detecta un evento táctil con esta vista como objetivo
- boolean onInterceptTouchEvent (MotionEvent ev): se llama cada vez que se detecta un evento táctil con este ViewGroup o un elemento secundario como objetivo. Si esta función devuelve verdadero, el MotionEvent será interceptado, lo que significa que no se transmitirá al niño, sino al onTouchEvent de esta Vista.
Pequeña respuesta:
onInterceptTouchEvent viene antes de setOnTouchListener.
Porque este es el primer resultado en Google. Quiero compartir con ustedes una excelente charla de Dave Smith en Youtube: dominar el sistema táctil de Android y las diapositivas están disponibles here . Me dio una buena comprensión profunda sobre el sistema táctil de Android:
Cómo maneja la actividad el tacto:
Activity.dispatchTouchEvent()
- Siempre primero en ser llamado
- Envía evento a la vista de raíz adjunta a la ventana
onTouchEvent()
- Llamado si no hay vistas consumen el evento.
- Siempre último en ser llamado
Cómo maneja la vista el tacto:
View.dispatchTouchEvent()
- Envía el evento al oyente primero, si existe
View.OnTouchListener.onTouch()
- Si no se consume, se procesa el propio toque.
View.onTouchEvent()
Cómo un grupo de vista maneja el tacto:
ViewGroup.dispatchTouchEvent()
onInterceptTouchEvent()
- Compruebe si debería 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 toque es relevante (vista interior),
child.dispatchTouchEvent()
- Si no es manejado por el anterior, despacha a la siguiente vista
- Si ningún niño maneja el evento, el oyente tiene una oportunidad
OnTouchListener.onTouch()
- Si no hay oyente, 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 llama a dispatchTouchEvent()
en cada capa de 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. ViewGroup
solo detendría el envío si ViewGroup
onInterceptTouchEvent()
-method devuelve true. La diferencia es que dispatchTouchEvent()
está enviando MotionEvents
y onInterceptTouchEvent
le dice si debe interceptar (no enviar el MotionEvent
a los niños) o no (enviar 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);
}
Puede 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 está lleno de ejemplos.
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 de retorno.
El envío de eventos en Android comienza desde Actividad-> Ver grupo-> Ver.
dispatchTouchEvent maneja antes de onInterceptTouchEvent.
Usando este sencillo ejemplo:
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 log será como:
I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent
Entonces, en caso de que esté trabajando con estos 2 manejadores, utilice dispatchTouchEvent para manejar en primera instancia el evento, que irá a onInterceptTouchEvent.
Otra diferencia es que si dispatchTouchEvent devuelve ''falso'', el evento no se propaga al hijo, en este caso el Texto de edición, mientras que si devuelve falso en onInterceptTouchEvent, el evento aún se enviará al Texto de edición
Respuesta corta: se dispatchTouchEvent()
primer lugar.
Consejo breve: no debe anular dispatchTouchEvent()
ya que es difícil de controlar, a veces puede ralentizar su rendimiento. En mi humilde opinión, sugiero anular onInterceptTouchEvent()
.
Debido a que la mayoría de las respuestas mencionan claramente el evento de toque de flujo en la actividad / vista de grupo / vista, solo agrego más detalles sobre el código de estos métodos en ViewGroup
(ignorando dispatchTouchEvent()
):
onInterceptTouchEvent()
se llamará primero, el evento ACTION se llamará respectivamente down -> move -> up. Hay 2 casos:
Si devuelve falso en 3 casos (ACTION_DOWN, ACTION_MOVE, ACTION_UP), considerará que el padre no necesitará este evento táctil , por lo que
onTouch()
de los padres nunca llama , peroonTouch()
de los niños llamará en su lugar ; Sin embargo, tenga en cuenta:- El
onInterceptTouchEvent()
aún continúa recibiendo el evento táctil, siempre que sus hijos no llamen arequestDisallowInterceptTouchEvent(true)
. - Si no hay niños que reciban ese evento (puede ocurrir en 2 casos: no hay niños en la posición que los usuarios tocan, o si hay niños pero devuelve falso en ACTION_DOWN), los padres enviarán ese evento a
onTouch()
del los padres
- El
A la inversa, si devuelve verdadero , el padre robará este evento táctil de inmediato, y
onInterceptTouchEvent()
se detendrá de inmediato, enonTouch()
lugar seonTouch()
de los padres, así como a todos losonTouch()
, recibirán el último evento de acción - ACTION_CANCEL (por lo tanto, significa que los padres robaron el evento táctil y los niños no pueden manejarlo desde ese momento). El flujo deonInterceptTouchEvent()
devuelve false es normal, pero hay una pequeña confusión con return true case, por lo que lo enumero aquí:- Devuelva verdadero en ACTION_DOWN,
onTouch()
de los padres recibirá de nuevo ACTION_DOWN y las siguientes acciones (ACTION_MOVE, ACTION_UP). - Devuelva verdadero en ACTION_MOVE,
onTouch()
de los padres recibirá el siguiente ACTION_MOVE (no es el mismo ACTION_MOVE enonInterceptTouchEvent()
) y las siguientes acciones (ACTION_MOVE, ACTION_UP). - Devuelva true en ACTION_UP,
onTouch()
de los padres NO llamará, ya que es demasiado tarde para que los padres roben el evento de toque.
- Devuelva verdadero en ACTION_DOWN,
Una cosa más importante es ACTION_DOWN del evento en onTouch()
determinará si la vista desea recibir más acción de ese evento o no. Si la vista devuelve verdadero en ACTION_DOWN en onTouch()
, significa que la vista está dispuesta a recibir más acción de ese evento. De lo contrario, devolver falso en ACTION_DOWN en onTouch()
implicará que la vista no recibirá más acciones de ese evento.
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume =false;
if(onInterceptTouchEvent(ev){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
}