2014-05-23 5 views
0

J'essaie d'implémenter un défilement bidirectionnel personnalisé avec le canevas Android et un GuestureDetector.SimpleOnGestureListener mais j'ai quelques problèmes. Il semble que le premier événement de défilement avec lui donne toujours un énorme saut inexact.Android onScroll événement donnant de mauvaises valeurs

Par exemple, si je clique au milieu de la toile et faites défiler un petit peu, je vais voir les événements de défilement comme celui-ci (arrondi légèrement):

scroll x: -352 scroll y: -373 
scroll x: -4 scroll y: 3 
scroll x -4 scroll y: 3 

La première valeur du défilement est toujours énorme saut que je ne suis pas à distance avec mon doigt je défile avec. Il semble que je traite mon premier mouvement de défilement comme si je déplaçais mon doigt d'un coin de la toile à l'endroit où se trouve le doigt?

Voici mon auditeur réel:

public class BoardScrollListener extends GestureDetector.SimpleOnGestureListener { 

    private GameService gameService = GameService.getInstance(); 
    private UiService uiService = UiService.getInstance(); 

    @Override 
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 

     Log.d("scroll", "scroll x: " + distanceX + " scroll y: " + distanceY); 
     if (distanceX > -150 && distanceY > -150) { 
      Game game = gameService.getGame(); 
      game.setxPixelOffset((int) (game.getxPixelOffset() - distanceX)); 
      game.setyPixelOffset((int) (game.getyPixelOffset() - distanceY)); 

      uiService.getGameboardActivity().getGameboard().invalidate(); 
     } 
     return true; 
    } 
    @Override 
    public boolean onDown(MotionEvent e) { 
     return true; 
    } 
} 

I Tried emballage en ce que si la vérification pour voir ce qui est arrivé, et cela résout effectivement le problème la plupart du temps, mais il est évidemment pas une vraie solution. Quelqu'un peut-il me dire pourquoi l'événement de mouvement est inexact, et un bon moyen de le rendre précis ou d'ignorer le premier mouvement d'un parchemin? J'utilise les fragments d'activité de support v4, donc j'ai aussi essayé de passer à GestureDetectorCompat, mais cela ne change rien. (Ceci est sur un appareil KitKat.)

+0

double question: http: // stackoverflow.com/questions/12465540/scroll-listener-premier-distancey-toujours-négatif-pour-vers le bas-scroll – Siva

Répondre

2

D'abord vous obtenez les coordonnées de ACTION_DOWN événement, puis la distance plus tard. Je pense que vous devriez faire une vérification de l'action de l'événement.

@Override 
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 

    Log.d("scroll", "scroll x: " + distanceX + " scroll y: " + distanceY); 

    // ! CHECK FOR EVENT ACTION ! 
    if (e2.getAction() == MotionEvent.ACTION_MOVE) { 
     Game game = gameService.getGame(); 
     game.setxPixelOffset((int) (game.getxPixelOffset() - distanceX)); 
     game.setyPixelOffset((int) (game.getyPixelOffset() - distanceY)); 

     uiService.getGameboardActivity().getGameboard().invalidate(); 
    } 
    return true; 
} 
+0

J'ai ajouté un message pour savoir si e2 est un 'ACTION_MOVE' à ma ligne de journal, et c'est toujours un 'ACTION_MOVE'. Je sens que mon problème doit avoir quelque chose à voir avec l'attachement de ce GestureListener à une toile de vue personnalisée? – CorayThan

+0

Eh bien, IDK alors, mais vous obtenez ces grandes valeurs lors du premier appel de onScroll car e1 est l'événement de mouvement ACTION_DOWN qui a commencé le défilement. –

+0

e1 est toujours nul. Les deux événements semblent être les mêmes dans toutes les circonstances. Je vois seulement les distances changer. – CorayThan

0

J'attachait séparément un auditeur onTouch qui a été à l'origine de cette question. Tirer le corps de cet événement dans l'écouteur surchargé onTouchEvent de ma vue personnalisée a résolu le problème.

1

J'ai rencontré le même problème. Dans mon cas, j'ai trouvé que c'était dû au fait que j'utilise onInterceptTouchEvent(). J'en ai besoin car un événement ACTION_DOWN devrait être géré par un enfant de mon groupe de vue. Toutefois, lors de l'interception d'un événement ACTION_MOVE et de son envoi à un GestureDetector, l'événement MOTION_DOWN précédant ACTION_MOVE n'est pas envoyé. Dans onScroll() du GestureListener e1 est toujours null.

Dans onScroll(), la toute première distanceX est calculée en fonction de la positionX de e1. Après cela, la position X du précédent e2 est utilisée. Si e1 est manquant onScroll() utilise 0 ou la position où ACTION_UP s'est produite du défilement précédent, si cela existe.

Quoi qu'il en soit, longue histoire courte. Pour le faire fonctionner, j'ai décidé d'ignorer le premier ACTION_MOVE dans onScroll(). Voir ci-dessous pour le code que j'ai utilisé.

@Override 
public boolean onInterceptTouchEvent(MotionEvent event) { 

    final int action = MotionEventCompat.getActionMasked(event); 

    // always handel touch event completion 
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 
     //Log.i("Intercept Up: ", "END GESTURE"); 
     mIsScrolling = false; 
     return false; 
    } 

    switch (action) { 
     case MotionEvent.ACTION_DOWN: { 
      // Record position 
      mDownX = event.getX(); 
      // let the child handel the event 
      return false; 
     } 
     case MotionEvent.ACTION_MOVE: { 
      if (mIsScrolling) { 
       // we are currently scrolling, so intercept the event 
       return true; 
      } 
      float dx = event.getX() - mDownX; 
      if (Math.abs(dx) > mTouchSlop) { 
       // We met the motion threshold; Intercept the event to initiate scrolling 
       mIsScrolling = true; 
       return true; 
      } 
      // Not scrolling and threshold not met 
      return false; 
     } 
     default: { 
      // other motion events will not be intercepted 
      return false; 
     } 
    } 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 

    final int action = MotionEventCompat.getActionMasked(event); 

    // always handel touch event completion 
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 
     mIsScrolling = false; 
     // Used to ignore the first scroll event with bad distanceX 
     mIsFirstScrollEvent = true; 
     return true; 
    } 
    // Use GestureDetector to detect event type 
    mGestureDetector.onTouchEvent(event); 
    return true; 
} 


private final GestureDetector.OnGestureListener mGestureListener = 
     new GestureDetector.SimpleOnGestureListener() { 

    @Override 
    public boolean onScroll (MotionEvent e1, MotionEvent e2, 
          float distanceX, float distanceY) { 

     // First scroll event should be ignored because of bad distanceX 
     if (mIsFirstScrollEvent) { 
      mIsFirstScrollEvent = false; 
      return true; 
     } 

     // Do the scroll stuff here 

     return true; 
    } 
}; 
0

J'ai le même problème. Mais dans mon cas, j'utilise différents OnGestureListener-s pour les méthodes onInterceptTouchEvent (...) et onTouchEvent (...).

donc je viens de définir un indicateur dans interception écouteur pour ignorer premier événement de défilement passé à toucher événement auditeur réel

private class GestureInterceptListener extends GestureDetector.SimpleOnGestureListener { 

    @Override 
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
     final boolean shouldIntercept = ...; // here we decide should we intercept this event, or should it be passed further 
     if (shouldIntercept) { 
      mGestureProcessListener.setFirstScrollEventIgnored(true); 
     } 
     return shouldIntercept; 
    } 

} 


private class GestureProcessListener extends GestureDetector.SimpleOnGestureListener { 

    private boolean ignoreFirstBuggyScrollEvent = false; 

    /** 
    * For some reason first scroll event on half-visible settings list gets strange incorrect negative values 
    * @param isIgnored - should next scroll event be ignored? value resets to false afterwards 
    */ 
    public void setFirstScrollEventIgnored(boolean isIgnored) { 
     ignoreFirstBuggyScrollEvent = isIgnored; 
    } 

    @Override 
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
     if (ignoreFirstBuggyScrollEvent) { 
      ignoreFirstBuggyScrollEvent = false; 
      return true; 
     } 
     ... // process scroll event here 
     return true; 
    } 

} 

Works pour moi

Questions connexes