web-dev-qa-db-fra.com

MotionEvent.ACTION_UP non appelé

Considérez le schéma suivant ci-dessous (pour une meilleure compréhension de mon problème) .enter image description here

Comme vous pouvez le constater, j’envisage une vue en liste entourée d’un remplissage. À présent, si un utilisateur appuie sur un élément listview, l’action que j’ai fournie lui fournit une couleur d’arrière-plan bleu clair. Maintenant, mon application traite elle-même des événements onTouch pour déterminer des actions telles que

  • Cliquez sur 
  • Balayage de gauche à droite
  • Balayage de droite à gauche

Voici mon code.

public boolean onTouch(View v, MotionEvent event) {
        if(v == null)
        {
            mSwipeDetected = Action.None;
            return false;
        }
        switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: {
            downX = event.getRawX();
            downY = event.getRawY();
            mSwipeDetected = Action.Start;

         // Find the child view that was touched (perform a hit test)
            Rect rect = new Rect();
            int childCount = listView.getChildCount();
            int[] listViewCoords = new int[2];
            listView.getLocationOnScreen(listViewCoords);
            int x = (int) event.getRawX() - listViewCoords[0];
            int y = (int) event.getRawY() - listViewCoords[1];
            View child;
            for (int i = 0; i < childCount; i++) {
                child = listView.getChildAt(i);
                child.getHitRect(rect);
                if (rect.contains(x, y)) {
                    mDownView = child;
                    break;
                }
            }


            return false; // allow other events like Click to be processed
        }
        case MotionEvent.ACTION_MOVE: {
            upX = event.getRawX();
            upY = event.getRawY();
            float deltaX=0,deltaY=0;
             deltaX = downX - upX;
             deltaY = downY - upY;

                if(deltaY < VERTICAL_MIN_DISTANCE)
                {
                            setTranslationX(mDownView, -(deltaX));
                            setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / listView.getWidth())));
                            return false;
                }
                else
                {
                    forceBringBack(v);
                }

                          return false;              

        }
        case MotionEvent.ACTION_UP:
        {

             stopX = event.getX();
             float stopValueY = event.getRawY() - downY;             
             float stopValue = stopX - downX;

             if(!mDownView.isPressed())
             {
                 forceBringBack(mDownView);
                 return false;
             }             

             boolean dismiss = false;
             boolean dismissRight = false;


             if(Math.abs(stopValue)<10)
             {
                 mSwipeDetected = Action.Start;
             }
             else
             {
                 mSwipeDetected = Action.None;

             }
             String log = "";
             Log.d(log, "Here is Y" + Math.abs(stopValueY));
             Log.d(log, "First Comparison of Stop Value > with/4" + (Math.abs(stopValue) > (listView.getWidth() /4)));
             Log.d(log, "Second Comparison " + (Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE));
             Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value  " + stopValue);

             if((Math.abs(stopValue) > (listView.getWidth() /4))&&(Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE))
             {
                 dismiss = true;
                 dismissRight = stopValue > 0;

                 if(stopValue>0)
                 {
                 mSwipeDetected = Action.LR;

                 }
                 else
                     mSwipeDetected = Action.RL;
             }
             Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value after dissmiss" + stopValue);

             if(dismiss)
             {
                 if(dismissRight)
                     mSwipeDetected = Action.LR;
                 else
                     mSwipeDetected = Action.RL;
                 animate(mDownView)
                 .translationX(dismissRight ? listView.getWidth() : - listView.getWidth())
                 .alpha(0)
                 .setDuration(mAnimationTime)
                 .setListener(new AnimatorListenerAdapter() {
                     public void onAnimationEnd(Animator animation)
                     {

                     }
                });
             }
             else
             {
                 animate(mDownView)
                 .translationX(0)
                 .alpha(1)
                 .setDuration(mAnimationTime)
                 .setListener(null);
             }


             break;           

        }
        }
        return false;
    }

Comme vous pouvez le constater, je détermine l'action effectuée dans MotionEvent.ACTION_UP et définit la valeur de Enum Action en conséquence. Cette logique fonctionne comme un charme si l'utilisateur ne franchit pas la limite de la vue liste. 

Désormais, si l'utilisateur glisse (ou spécifiquement), déplace son doigt le long de l'élément de liste, passe du bleu à l'orange, MotionEvent.ACTION_UP ne sera pas attribué à listview, ce qui empêchera mon code de prendre une décision et à cause de la traductionX () et setAlpha (), puisqu’aucune action n’est déterminée dans ce cas, cet élément de la liste devient vide. 

Le problème ne s'arrête pas là, car je ne gonfle pas la vue à chaque fois, la même ligne translateX () est gonflée à chaque fois, ce qui entraîne la multiplication d'un élément de liste vide/blanche. 

Y a-t-il quelque chose à faire pour que même si je ne rencontrais pas MotionEvent.ACTION_UP, je puisse toujours prendre une décision?

Merci.

55
Paras

Vous devriez return true; dans case MotionEvent.ACTION_DOWN:, pour que le MotionEvent.ACTION_UP soit traité.


Comme expliqué sur View.OnTouchListener :

Résultats:

True si l'auditeur a consommé l'événement, false sinon.

MotionEvent.ACTION_UP Ne sera pas appelé avant que le MotionEvent.ACTION_DOWN se soit produit, une explication logique à cela est qu'il est impossible pour un ACTION_UP de se produire si un ACTION_DOWN ne s'est jamais produit auparavant.

Cette logique permet au développeur de bloquer d'autres événements après ACTION_DOWN.

178
Danpe

Notez également que dans certaines circonstances (rotation d'écran, par exemple), le geste peut être annulé, auquel cas un MotionEvent.ACTION_UP ne sera PAS envoyé. À la place, un MotionEvent.ACTION_CANCEL est envoyé. Par conséquent, une instruction de commutateur d'action normale devrait ressembler à ceci:

switch (event.getActionMasked()) {
    case MotionEvent.ACTION_DOWN:
        // check if we want to handle touch events, return true
        // else don't handle further touch events, return false
    break;

    // ... handle other cases

    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_CANCEL:
        // finish handling touch events
        // note that these methods won't be called if 'false' was returned
        // from any previous events related to the gesture
    break;
}
20
TheIT

Je ne pense pas que l'ajout de return true;à la casse MotionEvent.ACTION_DOWN: finirait par résoudre le problème. Cela compliquait simplement la situation où return false aurait pu faire le travail comme un charme.

Ce qu'il faut remarquer, c'est que MotionEvent.ACTION_DOWN: /*something*/ return true; bloquera tous les autres rappels d'écouteur disponibles pour la vue, même onClickListenerm, alors que correctement return false dans MotionEvent.ACTION_UP: peut aider à propager l'événement MotionEvent à la bonne destination.

Référence à son code source original: https://github.com/romannurik/Android-swipetodismiss

6
Chrysanthemumer

Comme Danpe a expliqué dans sa réponse concise - J'ai dû ajouter le code ACTION_DOWN afin de faire reconnaître ACTION_UP.

            case MotionEvent.ACTION_DOWN:

                return true;

            case MotionEvent.ACTION_UP:

                XyPos xyPos = new XyPos();
                xyPos.x = last_x;
                xyPos.y = last_y;
                handleViewElementPositionUpdate(xyPos);

                break;

De toute façon, la méthode onTouch (..) entière est revenue, alors je ne suis pas sûre de savoir pourquoi cela ne suffisait pas ... 

0
Gene Bo