2011-05-31 7 views
15

Desde una perspectiva simplista, tengo una vista personalizada que contiene algunos mapas de bits que el usuario puede arrastrar y cambiar de tamaño.Anulación onTouchEvent que compite con ScrollView

La forma en que hago esto es bastante estándar como en onTouchEvent puedo reemplazar en mi CustomView y comprobar si el usuario está tocando dentro de una imagen, etc.

Mi problema viene cuando quiero colocar este CustomView en un ScrollView . Esto funciona, pero ScrollView y CustomView parecen competir por MotionEvents, es decir, cuando intento arrastrar una imagen, ésta se mueve lentamente o la vista se desplaza.

Estoy pensando que tendré que extender un ScrollView para poder anular On InterceptTouchEvent y dejarle saber si el usuario está dentro de los límites de una imagen para no intentar desplazarse. Pero como ScrollView está más arriba en la jerarquía, ¿cómo puedo acceder al estado actual de CustomView?

¿Hay una manera mejor?

+1

Creo que su vista personalizada debe aplicar onInterceptTouchEvent y no ScrollView el fin de decidir si se debe controlar el evento táctil en consecuencia y luego las manos del evento si no decide para manejarlo – Nadewad

Respuesta

38

Normalmente, Android utiliza una pulsación larga para iniciar un arrastre en casos como estos, ya que ayuda a desambiguar cuando el usuario tiene la intención de arrastrar un elemento o desplazar el contenedor del elemento. Pero si tiene una señal inequívoca cuando el usuario comienza a arrastrar un elemento, intente con getParent().requestDisallowInterceptTouchEvent(true) desde la vista personalizada cuando sepa que el usuario está comenzando a arrastrar. (Documentos para este método here). Esto evitará que ScrollView intercepte los eventos táctiles hasta el final del gesto actual.

+2

Muchas gracias, ¡funcionó! Hay un gran conocimiento de API aquí en SO :) – brk3

+1

Ayuda cuando las personas mantienen estas cosas en la plataforma respondiendo sus preguntas. ;) – adamp

+0

gracias por esto :) –

8

Ninguna de las soluciones encontradas trabajó "fuera de la caja" para mí, probablemente porque mi vista personalizada se extiende Ver, no ViewGroup, y por lo tanto no se puede aplicar onInterceptTouchEvent.

También llamando al getParent().requestDisallowInterceptTouchEvent(true) estaba lanzando NPE, o no haciendo nada en absoluto.

Por último, esta es la forma en que resolvió el problema:
Dentro de su llamada personalizada onTouchEventrequestDisallow... cuando su vista se hará cargo del caso. Por ejemplo:

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    Point pt = new Point((int)event.getX(), (int)event.getY()); 
    if (event.getAction() == MotionEvent.ACTION_DOWN) { 
     if (/*this is an interesting event my View will handle*/) { 
      // here is the fix! now without NPE 
      if (getParent() != null) { 
       getParent().requestDisallowInterceptTouchEvent(true); 
      } 

      clicked_on_image = true; 
     } 
    } else if (event.getAction() == MotionEvent.ACTION_MOVE) { 
     if (clicked_on_image) { 
      //do stuff, drag the image or whatever 
     } 
    } else if (event.getAction() == MotionEvent.ACTION_UP) { 
     clicked_on_image = false; 
    } 
    return true; 
} 

Ahora mi vista personalizada funciona bien, el manejo de algunos eventos y dejar ScrollView enredar a los que no se preocupan. Encontré la solución aquí: http://android-devblog.blogspot.com.es/2011/01/scrolling-inside-scrollview.html

Espero que ayude.

+0

, ¡eso ayudó! :) Pero sugiero que cambie la verificación NullPointer a: ViewParent parent = getParent(); if (parent! = Null) { parent.requestDisallowInterceptTouchEvent (true); } – netzpurist

+0

gracias esto funcionó perfectamente – Ryan

0

Hay un evento de Android llamado MotionEvent.ACTION_CANCEL (valor = 3). Todo lo que hago es anular el método onTouchEvent de mi control personalizado y capturar este valor. Si detecto esta condición, entonces respondo en consecuencia.

Aquí hay un código:

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    if(isTouchable) { 
     int maskedAction = event.getActionMasked();   
     if (maskedAction == MotionEvent.ACTION_DOWN) { 
      this.setTextColor(resources.getColor(R.color.octane_orange)); 
      initialClick = event.getX(); 
     } else if (maskedAction == MotionEvent.ACTION_UP) { 
      this.setTextColor(defaultTextColor); 
      endingClick = event.getX(); 
      checkIfSwipeOrClick(initialClick, endingClick, range); 
     } else if(maskedAction == MotionEvent.ACTION_CANCEL) 
      this.setTextColor(defaultTextColor); 
    } 
    return true; 
}