web-dev-qa-db-fra.com

Android: Comment détecter la fin d'un parchemin

J'utilise la méthode onScroll de GestureDetector.SimpleOnGestureListener pour faire défiler une grande image sur une toile. Lorsque le défilement est terminé, je souhaite redessiner le bitmap au cas où l'utilisateur souhaite faire défiler davantage ... au-delà du bord du bitmap, mais je ne vois pas comment détecter la fin du défilement (l'utilisateur a levé le doigt). de l'écran).

e2.getAction () semble toujours renvoyer la valeur 2, ce qui n’aide en rien . e2.getPressure semble renvoyer des valeurs assez constantes (environ 0,25) jusqu’à l’appel final de OnScroll, lorsque la pression semble tomber à environ 0,13. Je suppose que je pourrais détecter cette réduction de pression, mais ce sera loin d'être infaillible.

Il doit y avoir un meilleur moyen: quelqu'un peut-il aider, s'il vous plaît?

64
prepbgg

Voici comment j'ai résolu le problème. J'espère que cela t'aides.

// declare class member variables
private GestureDetector mGestureDetector;
private OnTouchListener mGestureListener;
private boolean mIsScrolling = false;


public void initGestureDetection() {
        // Gesture detection
    mGestureDetector = new GestureDetector(new SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            handleDoubleTap(e);
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            handleSingleTap(e);
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // i'm only scrolling along the X axis
            mIsScrolling = true;                
            handleScroll(Math.round((e2.getX() - e1.getX())));
            return true;
        }

        @Override
        /**
         * Don't know why but we need to intercept this guy and return true so that the other gestures are handled.
         * https://code.google.com/p/Android/issues/detail?id=8233
         */
        public boolean onDown(MotionEvent e) {
            Log.d("GestureDetector --> onDown");
            return true;
        }
    });

    mGestureListener = new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {

            if (mGestureDetector.onTouchEvent(event)) {
                return true;
            }

            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(mIsScrolling ) {
                    Log.d("OnTouchListener --> onTouch ACTION_UP");
                    mIsScrolling  = false;
                    handleScrollFinished();
                };
            }

            return false;
        }
    };

    // attach the OnTouchListener to the image view
    mImageView.setOnTouchListener(mGestureListener);
}
65
Akos Cz

Vous devriez jeter un oeil à http://developer.Android.com/reference/Android/widget/Scroller.html . Cela pourrait être utile (trié par pertinence):

isFinished();
computeScrollOffset();
getFinalY(); getFinalX(); and getCurrY() getCurrX()
getDuration()

Cela implique que vous deviez créer un Scroller.

Si vous souhaitez toucher, vous pouvez également utiliser GestureDetector et définir votre propre défilement de zone de travail. L'exemple suivant crée un ScrollableImageView. Pour l'utiliser, vous devez définir les mesures de votre image. Vous pouvez définir votre propre plage de défilement. Une fois le défilement terminé, l’image est redessinée. 

http://www.anddev.org/viewtopic.php?p=31487#31487

En fonction de votre code, vous devez envisager d'invalider (int, int, int, int b); pour l'invalidation.

4
user238801
SimpleOnGestureListener.onFling() 

Cela semble se produire quand un parchemin se termine (c’est-à-dire que l’utilisateur lâche le doigt), c’est ce que j’utilise et cela fonctionne très bien pour moi.

3
Aron Cederholm

Revenant à cela au bout de quelques mois, j’ai suivi une approche différente: utiliser un gestionnaire (comme dans l’exemple Android Snake) pour envoyer un message à l’application toutes les 125 millisecondes, lui demandant de vérifier si un défilement a été lancé et si plus de 100 millisecondes s'est écoulé depuis le dernier événement de défilement.

Cela semble fonctionner assez bien, mais si quelqu'un peut voir des inconvénients ou des améliorations possibles, je serais reconnaissant de les entendre.

Le code pertinent est dans la classe MyView:

public class MyView extends Android.view.View {

...

private long timeCheckInterval = 125; // milliseconds
private long scrollEndInterval = 100;
public long latestScrollEventTime;
public boolean scrollInProgress = false;

public MyView(Context context) {
    super(context);
}

private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();

class timeCheckHandler extends Handler{

        @Override
        public void handleMessage(Message msg) {
        long now = System.currentTimeMillis();
        if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval)) {
                    scrollInProgress = false;

// Scroll est terminé, alors insérez le code ici

// qui appelle la méthode doDrawing ()

// redessine l'image bitmap recentrée où le défilement s'est terminé

                    [ layout or view ].invalidate();
        }
        this.sleep(timeCheckInterval);
        }

        public void sleep(long delayMillis) {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
            }
    }
}

@Override protected void onDraw(Canvas canvas){
        super.onDraw(canvas);

// code permettant de dessiner un bitmap de tampon de grande taille sur le canevas de la vue// positionné pour prendre en compte tout défilement en cours

}

public void doDrawing() {

// code permettant d'effectuer un dessin détaillé (et fastidieux) // sur un grand bitmap de tampon

// l'instruction suivante réinitialise l'horloge de vérification du temps // l'horloge est démarrée pour la première fois lorsque// l'activité principale appelle cette méthode au démarrage de l'application

        mTimeCheckHandler.sleep(timeCheckInterval);
}

// reste de la classe MyView

et dans la classe MyGestureDetector

public class MyGestureDetector extends SimpleOnGestureListener {

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

    [MyView].scrollInProgress = true;
        long now = System.currentTimeMillis();  
    [MyView].latestScrollEventTime =now;

    [MyView].scrollX += (int) distanceX;
    [MyView].scrollY += (int) distanceY;

// l'instruction suivante provoque l'appel de la méthode onDraw de View// qui trace le bitmap du tampon sur l'écran// décalé pour prendre en compte le défilement

    [MyView].invalidate();

}

// reste de la classe MyGestureDetector

}

1
prepbgg

Je cherchais dans le même problème. J'ai vu Akos Cz répondre à votre question. J'ai créé quelque chose de similaire, mais avec ma version, j'ai remarqué que cela ne fonctionnait que pour un parchemin classique, c'est-à-dire qui ne génère pas de flou. Mais si un fling a été généré - peu importe si j'ai traité un fling ou non, il n'a PAS détecté le "ACTION_UP" dans "onTouchEvent". Maintenant peut-être que c'était juste quelque chose avec mon implémentation, mais si c'était le cas, je ne pourrais pas comprendre pourquoi.

Après une enquête plus approfondie, j'ai remarqué que, pendant une opération, "ACTION_UP" était transmis à "onFling" dans "e2" à chaque fois. J'ai donc pensé que ce devait être la raison pour laquelle il n'était pas traité dans "onTouchEvent" dans ces cas-là.

Pour que cela fonctionne pour moi, il me suffisait d'appeler une méthode pour gérer "ACTION_UP" dans "onFling", puis cela fonctionnait pour les deux types de défilement. Voici les étapes exactes que j'ai prises pour implémenter dans mon application:

-initialisé un booléen "gestureScrolling" sur "false" dans un constructeur.

-Je l'ai mis sur "true" dans "onScroll"

-créé une méthode pour gérer l'événement "ACTION_UP". À l'intérieur de cet événement, j'ai réinitialisé "gestureSCrolling" à false, puis j'ai effectué le reste du traitement que je devais effectuer.

-in "onTouchEvent", si un "ACTION_UP" a été détecté et que "gestureScrolling" = true, j'ai appelé ma méthode pour gérer "ACTION_UP".

-Et la partie que j'ai faite était différente: j'ai aussi appelé ma méthode pour gérer "ACTION_UP" dans "onFling".

1
Mike

C'est ce qui a fonctionné pour moi. 

J'ai enrichi la méthode existante GestureDetector.OnGestureListener with onFingerUp () . Cet écouteur fait tout comme le GestureDetector intégré et peut également écouter l'événement finger up (ce n'est pas onFling () car cela n'est appelé que lorsque le doigt est levé avec une action rapide de balayage).

import Android.content.Context;
import Android.os.Handler;
import Android.view.GestureDetector;
import Android.view.MotionEvent;

public class FingerUpGestureDetector extends GestureDetector {
    FingerUpGestureDetector.OnGestureListener fListener;
    public FingerUpGestureDetector(Context context, OnGestureListener listener) {
        super(context, listener);
        fListener = listener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, OnGestureListener fListener) {
        super(context, listener);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, OnGestureListener fListener) {
        super(context, listener, handler);
        this.fListener = fListener;
    }

    public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, boolean unused, OnGestureListener fListener) {
        super(context, listener, handler, unused);
        this.fListener = fListener;
    }

    public interface OnGestureListener extends GestureDetector.OnGestureListener {
        boolean onFingerUp(MotionEvent e);
    }

    public static class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements FingerUpGestureDetector.OnGestureListener {
        @Override
        public boolean onFingerUp(MotionEvent e) {
            return false;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (super.onTouchEvent(ev)) return true;
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            return fListener.onFingerUp(ev);
        }
        return false;
    }
}
1
pepan

Je suis sûr qu'il est trop tard pour vous, cependant, il semble que j'ai trouvé la bonne solution à votre question initiale et que l'intention ne soit pas nécessaire.

Si vous utilisez l'objet Scroller/OverScroller pour le défilement, vous devez vérifier la valeur de retour à l'aide de la fonction suivante.

public boolean computeScrollOffset() 

prendre plaisir

harvinder

1
resp78

Je pense que cela fonctionnera selon vos besoins

protected class SnappingGestureDetectorListener extends SimpleOnGestureListener{

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){
        boolean result = super.onScroll(e1, e2, distanceX, distanceY);

        if(!result){
            //Do what you need to do when the scrolling stop here
        }

        return result;
    }

}
0
nautilusvn

Ma tentative d'ajouter des fonctionnalités supplémentaires au détecteur de mouvements. J'espère que ça aide quelqu'un à mettre son temps à mieux utiliser ...

https://Gist.github.com/WildOrangutan/043807ddbd68978179b7cea3b53c71e8

0
Peter

Je ne l'ai pas fait moi-même, mais en regardant onTouch (), vous obtenez toujours une séquence 0 <2> 1, de sorte que la fin doit être un 1 pour le levage par le doigt.

0
Rob Kent

Extrait de l'événement onScroll de l'API GestureListener: texte du lien

public abstract boolean onScroll (MotionEvent e1, MotionEvent e2, float DistanceX, float distanceY) Depuis: API Niveau 1

Résultats * true si l'événement est consommé, sinon false

Peut-être qu'une fois l'événement consommé, l'action est terminée et l'utilisateur a retiré son doigt de l'écran ou tout au moins a terminé cette action onScroll

Vous pouvez ensuite l'utiliser dans une instruction IF pour rechercher == true, puis commencer par l'action suivante.

0
Tom 'Blue' Piddock

Je ne connais pas Android, mais en regardant la documentation, il semble que Rob a raison: Android ACTION_UP constant Essayez de rechercher ACTION_UP dans getAction ()?

Edit: Que montre e1.getAction ()? Renvoie-t-il jamais ACTION_UP? La documentation indique qu'elle contient l'événement initial initial, alors peut-être qu'il avertira également lorsque le pointeur est en place

Edit: Seulement deux choses auxquelles je peux penser. Vous retournez faux à un moment donné? Cela peut empêcher ACTION_UP

La seule autre chose que j'essaierais est d'avoir un événement séparé, peut-être onDown, et de définir un indicateur dans onScroll tel que isScrolling. Quand ACTION_UP est donné à onDown et que isScrolling est défini, vous pouvez faire ce que vous voulez et réinitialiser isScrolling à false. En d’autres termes, si onDown est appelé avec onScroll, et getAction renverra ACTION_UP lors de onDown.

0
Bob

je n'ai pas essayé/utilisé cela, mais une idée pour une approche:

arrête/interrompt le canevas de redessinage sur TOUS les événements de défilement, attendez 1s, puis commencez à redessiner les canevas de CHAQUE scroll  

cela conduira à effectuer le rafraîchissement uniquement à la fin du défilement, car seul le dernier défilement sera réellement ininterrompu pour que le rafraîchissement soit terminé.

espérons que cette idée vous aide :)

0
b0x0rz