2011-01-13 3 views
10

J'ai une activité qui contient un ViewFlipper. Le ViewFlipper comprend 2 mises en page, qui sont essentiellement des ListViews. Donc, l'idée ici est que j'ai deux listes et pour naviguer de l'une à l'autre, j'utiliserais un balayage horizontal.Problème lors de la détection de gestes sur ListView

J'ai cela qui fonctionne. Cependant, quel que soit l'élément de liste sur lequel se trouve votre doigt lorsque le balayage commence à s'exécuter, cet élément sera également cliqué longuement.

Voici le code approprié je:

public class MyActivity extends Activity implements OnItemClickListener, OnClickListener { 

    private static final int SWIPE_MIN_DISTANCE = 120; 
    private static final int SWIPE_MAX_OFF_PATH = 250; 
    private static final int SWIPE_THRESHOLD_VELOCITY = 200; 

    private GestureDetector mGestureDetector; 
    View.OnTouchListener mGestureListener; 

    class MyGestureDetector extends SimpleOnGestureListener { 
     @Override 
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
      try { 
       if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) 
        return false; 
       // right to left swipe 
       if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { 
        if (mCurrentScreen != SCREEN_SECONDLIST) { 
         mCurrentScreen = SCREEN_SECONDLIST; 
         mFlipper.setInAnimation(inFromRightAnimation()); 
         mFlipper.setOutAnimation(outToLeftAnimation()); 
         mFlipper.showNext(); 
         updateNavigationBar(); 
        } 
       } 
       else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { 
        if (mCurrentScreen != SCREEN_FIRSTLIST) { 
         mCurrentScreen = SCREEN_FIRSTLIST; 
         mFlipper.setInAnimation(inFromLeftAnimation()); 
         mFlipper.setOutAnimation(outToRightAnimation()); 
         mFlipper.showPrevious(); 
         updateNavigationBar(); 
        } 
       } 
      } catch (Exception e) { 
       // nothing 
      } 
      return true; 
     } 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     if (mGestureDetector.onTouchEvent(event)) 
      return true; 
     else 
      return false; 
    } 





    ViewFlipper mFlipper; 

    private int mCurrentScreen = SCREEN_FIRSTLIST; 

    private ListView mList1; 
    private ListView mList2; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     requestWindowFeature(Window.FEATURE_NO_TITLE); 
     setContentView(R.layout.layout_flipper); 

     mFlipper = (ViewFlipper) findViewById(R.id.flipper); 

     mGestureDetector = new GestureDetector(new MyGestureDetector()); 
     mGestureListener = new View.OnTouchListener() { 
      public boolean onTouch(View v, MotionEvent event) { 
       if (mGestureDetector.onTouchEvent(event)) { 
        return true; 
       } 
       return false; 
      } 
     }; 

     // set up List1 screen 

     mList1List = (ListView)findViewById(R.id.list1); 
     mList1List.setOnItemClickListener(this); 
     mList1List.setOnTouchListener(mGestureListener); 

     // set up List2 screen 
     mList2List = (ListView)findViewById(R.id.list2); 
     mList2List.setOnItemClickListener(this); 
     mList2List.setOnTouchListener(mGestureListener); 

    } 

    … 
} 

Si je change le "return true;" déclaration de la GestureDetector à "return false;", je ne reçois pas de clics longs. Malheureusement, je reçois des clics réguliers.

Est-ce que quelqu'un sait comment je peux contourner cela?

Répondre

7

juste jeter dans une réponse complètement différente avec une approche complètement différente ...

J'ai fait une vue personnalisée appelée SwipeView, il est open source etc etc bla bla bla. Il devrait vous permettre de faire ce que vous voulez faire aussi simplement que d'aller:

mSwipeView.addChild(myList1); 
mSwipeView.addChild(myList2); 

Vous n'aurez pas à déconner avec des détecteurs de geste ou quoi que ce soit, et le coup doit suivre votre doigt comme vous le faites .. (Ceci se lit comme une terrible publicité terrible, s'il vous plaît excusez-moi.Ça a été une longue journée;)

Avoir des liens merveilleux:

http://jasonfry.co.uk/?id=23

http://jasonfry.co.uk/?id=24

http://jasonfry.co.uk/?id=28

+0

Ceci est similaire à un composant que j'ai récemment développé, sauf que j'ai ajouté le recyclage de la vue à la mienne et sa soutenu par un listAdapter. Fondamentalement, j'ai kangé un tas de code ListView et attaché à un mouvement personnalisé HorizontalScrollView comme vous l'avez fait. – jfelectron

+0

Oui, je travaille aussi sur l'ajout de SwipeView! Aussi, j'ai juste (littéralement juste, environ 2 minutes) remarqué qu'il y a un bug avec ListViews sur un SwipeView. J'ai maintenant corrigé le bug et mis à jour le repo git. https://github.com/fry15/uk.co.jasonfry.android.tools – jsonfry

+3

jfelectron est à vous sur GIThub? –

0

[EDIT] Scratch cela, totalement faux.

Je pense que vous devriez mettre à jour cette partie, et détecter si vous faisiez défiler vers la gauche ou la droite, et retourner vrai si vous étiez dans un état de balayage. Ou du moins c'est là que je regarderais en premier.

mGestureListener = new View.OnTouchListener() { 
      public boolean onTouch(View v, MotionEvent event) { 
       if (mGestureDetector.onTouchEvent(event)) { 
        return true; 
       } 
       return false; 
      } 
     }; 

Poursuivant avec mon pauvre exemple, désolé.

Dans l'onTouch, je crois que vous pouvez tester event.getAction() et déterminer si un clic prolongé s'est produit. Si c'est le cas, et que vous êtes dans un mouvement lancinant, alors revenez vrai pour capturer le long clic. Je crains que ce soit plus une suggestion qu'une réponse définitive.

Vérifiez également l'autre [méthodes] que vous pouvez remplacer à partir de SimpleOnGestureListener. Juste vérifié et

@Override 
public void onLongPress(MotionEvent e) { 
    // TODO Auto-generated method stub 
    super.onLongPress(e); 
} 

peut être quelque chose que vous pourriez expérimenter avec.

+0

C'est ce qui se passe. Ou est votre exemple ce que votre EDIT a l'intention d'invalider? – Andrew

+0

Désolé, c'est un exemple terrible, réparant un autre problème. Je vais essayer de mieux expliquer plus tard. – Emile

1

Puisque vous utilisez un GestureDetector, la chose à faire est de mettre en œuvre SimpleGestureDetector.onLongPress(MotionEvent e) pour gérer les longs clics. Cependant, vous ne pouvez pas connaître la liste qui a été pressée depuis longtemps, vous devez donc vous en souvenir à partir du onItemLongClick() normal.

class MyGestureDetector extends SimpleOnGestureListener { 
    ... 
    @Override public void onLongPress(MotionEvent e) { 
     if (longClickedItem != -1) { 
      Toast.makeText(MainActivity.this, "Long click!", Toast.LENGTH_LONG).show(); 
     }   
    } 
} 

int longClickedItem = -1; 

@Override 
public boolean onItemLongClick(AdapterView<?> list, View view, int position, long id) { 
    longClickedItem = position; 
    return true; 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 

    // Reset longclick item if new touch is starting 
    if (event.getAction()==MotionEvent.ACTION_DOWN) { 
     longClickedItem = -1; 
    } 

    if (mGestureDetector.onTouchEvent(event)) 
     return true; 
    else 
     return false; 
} 

J'ai testé cela et cela semble fonctionner correctement.

+0

Merci pour votre réponse. Je vais essayer. Je devrais noter que si une pression longue est ce qui est activé et est ce que j'ai noté, ce que je voudrais vraiment arriver est n'avoir aucune fuite de geste si quelqu'un balaye. Il y a quelques autres problèmes, en particulier avec les téléphones Android qui peuvent faire les animations de fin de liste iPhone et autres. Le vrai problème ici est que le balayage envoie d'autres gestes à la liste; et je ne sais pas comment arrêter ça. – Andrew

+0

Je viens d'essayer ceci et cela empêche le long clic de s'exécuter lorsque je balaye, bien que l'élément sur lequel le doigt provient soit surligne le jaune; – Andrew

+0

Essayez d'appeler list.cancelLongPress() si le détecteur de mouvement retourne vrai ... –

1

Allez-vous votre gestureDetector et ensemble

mGestureDetector.setIsLongpressEnabled(false); 

Cela empêchera les événements de longue durée d'être détectés.

0

J'ai développé un composant personnalisé dérivé de horizontalScrollView qui le fait. J'ai ListViews comme l'enfant voit et balayez sans appui long. Il est adossé à un listAdapter, qui peut être tout ce que vous voulez (ArrayAdapter, CursorAdapter ...) .Il charge jusqu'à trois vues enfants, puis en les retournant, il suffit de les échanger de chaque côté. La plupart de ces éléments sont extraits de ListView et doivent être refactorisés. C'est trop long pour poster directement ici.

http://pastie.org/1480091

0

Retirez votre getListView().setOnItemLongClickListener

Ajouter dans votre classe MyGestureDetector:

public void onLongPress(MotionEvent e) { 
    final int position = getListView().pointToPosition((int) e.getX(), 
      (int) e.getY()); 
    // what you want do 
    super.onLongPress(e); 
} 
Questions connexes