11

Possible Duplicate:
Gesture detection and ScrollView issue

EDIT: question with full code asked here.


I've got a layout with a child. I set a gesture listener to detect horizontal swipe on the layout. When the layout is a LinearLayout the swipe is properly detected, but when it's a ScrollView, it's not. I guess the gesture is first detected by the ScrollView and is not propagated to its ascendants, but I don't know how to solve it.

Here's my layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical">
    <ImageView android:layout_width="320dp" android:layout_height="30dp"
            android:src="@drawable/header"/>
    <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content">
        <!-- stuff -->
    </ScrollView>
</LinearLayout>

I set the following listener to my layout:

class ProductGestureListener extends SimpleOnGestureListener {

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

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

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

        try {
            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                return false;
            if(e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {                   
                // show previous item
            }  else if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
               // show next item
            }
        } catch (Exception e) {
        }           
        return false;
    }
}
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
jul
  • 36,404
  • 64
  • 191
  • 318
  • this question should provide the answer http://stackoverflow.com/questions/2646028/android-horizontalscrollview-within-scrollview-touch-handling – triggs Nov 30 '11 at 13:23
  • I tried it but the swipe on the layout is still not detected. – jul Nov 30 '11 at 13:26
  • I used the answer from that link a few days ago and it worked for me, can you post your layout file and relevant code? – triggs Nov 30 '11 at 13:30
  • I added the code of the gesture listener set to my layout. – jul Nov 30 '11 at 13:50
  • i didn't mean attach the gesture detector to the layout, imeant post layout xml file but i think i see the problem, i'll post in an answer – triggs Nov 30 '11 at 14:07
  • My layout is very simple, I've added it to my question. – jul Nov 30 '11 at 14:22

3 Answers3

18

If you want the whole Activity to be swipeable horizontally you can use the following as a super class for your Activity:

public abstract class SwipeActivity extends Activity {
   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 gestureDetector;

   @Override
   protected void onCreate( Bundle savedInstanceState ) {
      super.onCreate( savedInstanceState );
      gestureDetector = new GestureDetector( new SwipeDetector() );
   }

   private class SwipeDetector extends SimpleOnGestureListener {
      @Override
      public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) {

         // Check movement along the Y-axis. If it exceeds SWIPE_MAX_OFF_PATH,
         // then dismiss the swipe.
         if( Math.abs( e1.getY() - e2.getY() ) > SWIPE_MAX_OFF_PATH )
            return false;

         // Swipe from right to left.
         // The swipe needs to exceed a certain distance (SWIPE_MIN_DISTANCE)
         // and a certain velocity (SWIPE_THRESHOLD_VELOCITY).
         if( e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs( velocityX ) > SWIPE_THRESHOLD_VELOCITY ) {
            next();
            return true;
         }

         // Swipe from left to right.
         // The swipe needs to exceed a certain distance (SWIPE_MIN_DISTANCE)
         // and a certain velocity (SWIPE_THRESHOLD_VELOCITY).
         if( e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs( velocityX ) > SWIPE_THRESHOLD_VELOCITY ) {
            previous();
            return true;
         }

         return false;
      }
   }

   @Override
   public boolean dispatchTouchEvent( MotionEvent ev ) {
      // TouchEvent dispatcher.
      if( gestureDetector != null ) {
         if( gestureDetector.onTouchEvent( ev ) )
            // If the gestureDetector handles the event, a swipe has been
            // executed and no more needs to be done.
            return true;
      }
      return super.dispatchTouchEvent( ev );
   }

   @Override
   public boolean onTouchEvent( MotionEvent event ) {
      return gestureDetector.onTouchEvent( event );
   }

   protected abstract void previous();

   protected abstract void next();
}

All you need to do is implement the next() and previous() methods after extending SwipeActivity.

kaspermoerch
  • 16,127
  • 4
  • 44
  • 67
10

I had to add

@Override
public boolean dispatchTouchEvent(MotionEvent ev){
    super.dispatchTouchEvent(ev);    
    return productGestureDetector.onTouchEvent(ev); 
}

Add this method in your activity class which uses swiper just like the onCreate method.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
jul
  • 36,404
  • 64
  • 191
  • 318
1

The sequence of events for android is this

user performs some action -> action is passed to parent ->if parent handles action then the action is consumed ->else the action is passed to a child ->if child handles action then the action is consumed ->else the action is passed on

this process continues until the action is consumed or all children have received the action and none of them handled it.

To detect a horizontal swipe in a scroll view and pass it to a child instead of the scroll view consuming it the event needs to be intercepted.

ie user performs some action -> action is passed to parent ->if horizontal swipe pass to child -> else have the scroll view handle the action

to do this (as out lined in the top answer here: HorizontalScrollView within ScrollView Touch Handling ) a gesture detector is used in the scroll view for the sole purpose of detecting whether the gesture is horizontal or vertical.

If the gesture is horizontal then we want to intercept the event and pass it to the child.

You need to create a custom scrollview then implement a gesture detector there and call it from onInterceptTouch(). By returning true or false here we can say whether or not to consume the event here, everything you need is in that link above.

Community
  • 1
  • 1
triggs
  • 5,890
  • 3
  • 32
  • 31
  • If I replace the ScrollView by a LinearLayout, this works, but with a ScrollView, the gesture listener on the parent is never triggered... – jul Nov 30 '11 at 16:25
  • where exactly are you putting your gesture detector? – triggs Nov 30 '11 at 16:45
  • I asked a new question with all my code: http://stackoverflow.com/questions/8330187/gesture-detection-and-scrollview-issue – jul Nov 30 '11 at 17:22