Manejo de MotionEvent en ScrollView en Android

He estado tratando de averiguar el comportamiento de MotionEvents en ScrollViews en Android y hay algo que no puedo entender.

Como ejemplo he hecho una actividad que tiene un ScrollView dentro de él y el ScrollView tiene un LinearLayout dentro de él. Implementé mis propias clases para tener control sobre las funciones relacionadas con el tacto:

public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyInnerLayout inner = new MyInnerLayout(getApplicationContext()); MyLayout layout = new MyLayout(getApplicationContext()); layout.addView(inner,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); setContentView(layout); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("scrollview","activity dispatchTouchEvent "+ev.getAction()); return super.dispatchTouchEvent(ev); }; @Override public boolean onTouchEvent(MotionEvent ev) { Log.i("scrollview","activity on touch "+ev.getAction()); return super.onTouchEvent(ev); } public class MyLayout extends ScrollView { public MyLayout(Context context) { super(context); } @Override public boolean dispatchKeyEvent(KeyEvent ev) { Log.i("scrollview","layout dispatchKeyEvent "+ev.getAction()); return super.dispatchKeyEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("scrollview","layout onInterceptTouchEvent "+ev.getAction()); return false; } @Override public boolean onTouchEvent(MotionEvent ev) { Log.i("scrollview","layout on touch "+ev.getAction()); return false; } } public class MyInnerLayout extends LinearLayout{ public MyInnerLayout(Context context) { super(context); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("scrollview","inner layout dispatchTouchEvent "+ev.getAction()); return true; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.i("scrollview","inner layout onInterceptTouchEvent "+ev.getAction()); return true; } @Override public boolean onTouchEvent(MotionEvent ev) { Log.i("scrollview","inner layout on touch "+ev.getAction()); return true; } } } 

Cuando hago clic en cualquier parte de la pantalla, obtengo este registro:

 10-14 18:11:48.631: I/scrollview(14906): activity dispatchTouchEvent 0 10-14 18:11:48.631: I/scrollview(14906): layout onInterceptTouchEvent 0 10-14 18:11:48.631: I/scrollview(14906): layout on touch 0 10-14 18:11:48.631: I/scrollview(14906): activity on touch 0 10-14 18:11:48.647: I/scrollview(14906): activity dispatchTouchEvent 1 10-14 18:11:48.647: I/scrollview(14906): activity on touch 1 

eso significa que el evento táctil no hizo el camino hasta el diseño interior dentro de la vista de desplazamiento. sin embargo, cuando cambio el ScrollView a un LinearLayout (simplemente cambielo en el extend), el evento se reduce al diseño interno:

 10-14 18:24:08.975: I/scrollview(15115): activity dispatchTouchEvent 0 10-14 18:24:08.975: I/scrollview(15115): layout onInterceptTouchEvent 0 10-14 18:24:08.975: I/scrollview(15115): inner layout dispatchTouchEvent 0 10-14 18:24:09.045: I/scrollview(15115): activity dispatchTouchEvent 1 10-14 18:24:09.045: I/scrollview(15115): layout onInterceptTouchEvent 1 10-14 18:24:09.045: I/scrollview(15115): inner layout dispatchTouchEvent 1 

Busqué en el código fuente de la clase ScrollView y los únicos métodos relacionados con el tacto que anula son los que me sobrepuse. Así que no entiendo qué hace la diferencia entre el comportamiento de LinearLayout y ScrollView.

Tal vez ya averiguó por qué el comportamiento anterior, pero sólo en caso de que no, aquí va la razón.

Visión de conjunto

El onInterceptTouchEvent() se llaman de arriba abajo (de padre a hijo) permitiendo que una vista intercepte el evento de movimiento antes de ser manejado por un niño.

El onTouchEvent() se llaman down-top (de niño a padre) hasta que uno de ellos lo consume y termina el ciclo.

Un ScrollView intercepta MotionEvent para comprobar si debe desplazar la vista antes de pasarlos al niño. Si el rollo debe realizarse, el evento se consumirá y la vista secundaria no verá nada.

En el caso de LinearLayout , no hay ninguna razón por la que el evento se debe consumir durante onInterceptTouchEvent() , y siempre se pasa a la vista secundaria.

Qué está pasando en su código

Debido a que MyInnerLayout está vacío, ScrollView siempre está consumiendo el objeto MotionEvent .

Si, por ejemplo, establece el backgound del diseño interno de la siguiente manera:

  MyInnerLayout inner = new MyInnerLayout(getApplicationContext()); inner.setBackground(getResources().getDrawable(R.drawable.ic_launcher)); MyLayout layout = new MyLayout(getApplicationContext()); 

verá que si toca la imagen de fondo el evento llegará al niño. Si toca fuera de la imagen de fondo, el evento será consumido por ScrollView .

Espero que esto ayude.

Saludos.

Tengo vistas con animaciones en la vista de desplazamiento. Las animaciones strat y stop son iguales al evento de movimiento (DOWN y UP). Puedo arreglar la misma solución:

 public class RootActivity extends Activity implements OnTouchListener { private View tochedView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_root); ((View) findViewById(R.id.btnOther)).setOnTouchListener(this); ScrollView scroll = (ScrollView) findViewById(R.id.scrollView); scroll.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP && tochedView != null) { Log.i("Touche", "ScrollView ACTION_UP"); Animation upAnim = AnimationUtils.loadAnimation(RootActivity.this, R.anim.btn_up_anim); tochedView.startAnimation(upAnim); tochedView = null; return true; } return false; } }); } private void animateView(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { Log.i("Touche", "ACTION_DOWN"); Animation downAnim = AnimationUtils.loadAnimation(this, R.anim.btn_down_anim); v.startAnimation(downAnim); tochedView = v; } if (event.getAction() == MotionEvent.ACTION_UP && tochedView != null) { Log.i("Touche", "ACTION_UP"); Animation upAnim = AnimationUtils.loadAnimation(this, R.anim.btn_up_anim); v.startAnimation(upAnim); tochedView = null; } } @Override public boolean onTouch(View v, MotionEvent event) { animateView(v, event); } 

}

  • Cómo detectar el contacto del usuario con el espacio telefónico usando JS
  • Android cuál es la diferencia entre getAction () y getActionMasked () en MotionEvent
  • ¿Cómo detectar el número de dedos utilizados?
  • JQuery Arrastrar y soltar en dispositivos táctiles (iPad, Android)
  • Singletap touch detect en método Ontouch de la vista
  • ¿Qué es una solución alternativa para Chrome para el comportamiento incorrecto de clientX y clientY de Android?
  • Arrastre con velocidad en el juego Unity no igual dependiendo de la resolución
  • La propiedad webkitForce del evento táctil: Feedback de presión de los dedos. Buscando documentación
  • Android Move ImageView
  • Android ImageView Escalado y traducción del problema
  • Añadir eventos táctiles a la clase VrPanoramaView de SDK de cartón google para android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.