web-dev-qa-db-fra.com

Détecter une pression sur une touche, une pression longue sur un mouvement?

Je suis actuellement en train de bricoler avec la programmation de Android, mais j'ai un petit problème pour détecter différents événements tactiles, à savoir une pression tactile normale (appuyez sur l'écran et relâchez immédiatement), une pression longue ( touchez l'écran et maintenez le doigt dessus) et le mouvement (en faisant glisser sur l'écran).

Ce que je voulais faire, c'est avoir une image (d'un cercle) sur mon écran que je peux faire glisser. Puis, lorsque j'appuie une fois dessus (pression courte/normale), un Toast fournit des informations de base à ce sujet. Lorsque j'appuie longuement dessus, un AlertDialog avec une liste apparaît pour sélectionner une autre image (cercle, rectangle ou triangle).

J'ai créé une vue personnalisée avec mon propre OnTouchListener pour détecter les événements et dessiner l'image dans onDraw. OnTouchListener.onTouch ressemble à ceci:

// has a touch press started?
private boolean touchStarted = false;
// co-ordinates of image
private int x, y;

public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
        touchStarted = true;
    }
    else if (action == MotionEvent.ACTION_MOVE) {
        // movement: cancel the touch press
        touchStarted = false;

        x = event.getX();
        y = event.getY();

        invalidate(); // request draw
    }
    else if (action == MotionEvent.ACTION_UP) {
        if (touchStarted) {
            // touch press complete, show toast
            Toast.makeText(v.getContext(), "Coords: " + x + ", " + y, 1000).show();
        }
    }

    return true;
}

Le problème est que la presse ne fonctionne pas tout à fait comme prévu. En effet, lorsque je touche l'écran avec désinvolture, il détecte également un tout petit mouvement, annule la pression du toucher et déplace l'image à la place.

J'ai "piraté" un peu cette technique en introduisant une nouvelle variable "mTouchDelay" que j'ai mise à 0 dans ACTION_DOWN, j'augmente MOVE et si c'est> = 3 dans MOVE, j'exécute mon code "move". Mais j'ai le sentiment que ce n'est pas vraiment la voie à suivre.

Je n'ai pas non plus découvert comment détecter un appui long. Le coupable est vraiment le mouvement qui semble toujours déclencher.

Pour un exemple de ce que je veux à peu près, voir Android application "DailyStrip": il affiche une image d'une bande dessinée. Vous pouvez la faire glisser si elle est trop grande pour l'écran. Vous pouvez appuyer sur une fois pour certaines commandes à faire apparaître et appuyez longuement pour un menu d'options.

PS J'essaie de le faire fonctionner Android 1.5, car mon téléphone ne fonctionne que sur 1.5.

64
pbean

Ce code permet de distinguer entre le clic et le mouvement (glisser, faire défiler). Dans onTouchEvent, définissez un indicateur isOnClick et les coordonnées initiales X, Y sur ACTION_DOWN. Désactivez le drapeau dans ACTION_MOVE (sachant qu'un mouvement involontaire est souvent détecté et peut être résolu avec un const THRESHOLD).

private float mDownX;
private float mDownY;
private final float SCROLL_THRESHOLD = 10;
private boolean isOnClick;

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mDownX = ev.getX();
            mDownY = ev.getY();
            isOnClick = true;
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            if (isOnClick) {
                Log.i(LOG_TAG, "onClick ");
                //TODO onClick code
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (isOnClick && (Math.abs(mDownX - ev.getX()) > SCROLL_THRESHOLD || Math.abs(mDownY - ev.getY()) > SCROLL_THRESHOLD)) {
                Log.i(LOG_TAG, "movement detected");
                isOnClick = false;
            }
            break;
        default:
            break;
    }
    return true;
}

GestureDetector est la solution idéale pour LongPress, comme indiqué ci-dessus. Vérifiez ce Q & A:

Détecter un appui long avec Android

92
MSquare

De la Android Docs -

onLongClick ()

À partir de View.OnLongClickListener. Ceci est appelé lorsque l'utilisateur touche et maintient l'élément (en mode tactile), ou se concentre sur l'élément avec les touches de navigation ou la boule de commande et appuie et maintient enfoncée la touche "entrée" appropriée ou maintient enfoncée la boule de commande ( pendant une seconde).

onTouch ()

À partir de View.OnTouchListener. Ceci est appelé lorsque l'utilisateur exécute une action qualifiée d'événement tactile, y compris une presse, un relâchement ou tout geste de mouvement à l'écran (dans les limites de l'élément).

En ce qui concerne le "déplacement se produit même lorsque je touche", je définirais un delta et je m'assurerais que la vue a été déplacée d'au moins le delta avant de donner le coup d'envoi dans le code de mouvement. Si ce n'est pas le cas, lancez le code tactile.

20
Jason L.

J'ai découvert cela après beaucoup d'expérimentation.

Dans l'initialisation de votre activité:

setOnLongClickListener(new View.OnLongClickListener() {
  public boolean onLongClick(View view) {
    activity.openContextMenu(view);  
    return true;  // avoid extra click events
  }
});
setOnTouch(new View.OnTouchListener(){
  public boolean onTouch(View v, MotionEvent e){
    switch(e.getAction & MotionEvent.ACTION_MASK){
      // do drag/gesture processing. 
    }
    // you MUST return false for ACTION_DOWN and ACTION_UP, for long click to work
    // you can return true for ACTION_MOVEs that you consume. 
    // DOWN/UP are needed by the long click timer.
    // if you want, you can consume the UP if you have made a drag - so that after 
    // a long drag, no long-click is generated.
    return false;
  }
});
setLongClickable(true);
15
Sanjay Manohar

Si vous avez besoin de distinguer entre un clic, une longue presse et un défilement, utilisez GestureDetector

L'activité implémente GestureDetector.OnGestureListener

puis créez un détecteur dans onCreate par exemple

mDetector = new GestureDetectorCompat(getActivity().getApplicationContext(),this);

puis éventuellement setOnTouchListener sur votre vue (par exemple webview) où

onTouch(View v, MotionEvent event) {
return mDetector.onTouchEvent(event);
}

et maintenant vous pouvez utiliser Override onScroll, onFling, showPress (détecter un appui long) ou onSingleTapUp (détecter un clic)

3
voytez

Je cherchais une solution similaire et voici ce que je suggérerais. Dans la méthode OnTouch, enregistrez l'heure pour l'événement MotionEvent.ACTION_DOWN, puis, pour MotionEvent.ACTION_UP, enregistrez l'heure à nouveau. De cette façon, vous pouvez également définir votre propre seuil. Après quelques essais, vous connaîtrez le temps maximum en millis. Il vous faudra enregistrer une simple pression et vous pourrez l’utiliser en déplacement ou selon une autre méthode à votre guise.

J'espère que cela a aidé. Veuillez commenter si vous avez utilisé une méthode différente et résolu votre problème.

3
rajankz

Je pense que vous devriez implémenter GestureDetector.OnGestureListener comme décrit dans tilisation de GestureDetector pour détecter les événements tactiles longs, doubles pressions, défilement ou autres événements tactiles dans Android et androidsnippets , puis implémenter la logique du robinet dans onSingleTapUp et déplacer la logique dans les événements onScroll

2
Lord_JABA

il fallait juste un événement ontouch dans la classe de superposition. vérifier ça, ça m'a aidé

http://mirnauman.wordpress.com/2012/04/16/Android-google-maps-tutorial-part-6-getting-the-location-that-is-touched/

0
Nakul Sudhakar

GestureDetector.SimpleOnGestureListener a des méthodes pour aider dans ces 3 cas;

   GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {

        //for single click event.
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            return true;
        }

        //for detecting a press event. Code for drag can be added here.
        @Override
        public void onShowPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clipData = ClipData.newPlainText("..", "...");
            clipboardManager.setPrimaryClip(clipData);

            ConceptDragShadowBuilder dragShadowBuilder = new CustomDragShadowBuilder(child);
            // drag child view.
            child.startDrag(clipData, dragShadowBuilder, child, 0);
        }

        //for detecting longpress event
        @Override
        public void onLongPress(MotionEvent e) {
            super.onLongPress(e);
        }
    });
0
Subhanandh

J'étais justement en train de gérer ce gâchis après avoir voulu que longclick ne se termine pas par un événement de clic.

Voici ce que j'ai fait.

public boolean onLongClick(View arg0) {
    Toast.makeText(getContext(), "long click", Toast.LENGTH_SHORT).show();
    longClicked = true;
    return false;
}

public void onClick(View arg0) {
    if(!longClicked){
        Toast.makeText(getContext(), "click", Toast.LENGTH_SHORT).show();
    }
    longClick = false; // sets the clickability enabled
}

boolean longClicked = false;

C'est un peu un bidouillage mais ça marche.

0
Chris Sullivan