24

The best I could find on this particular issue (although I do not use a Gallery): ScrollView and Gallery interfering - doesn't really give a specific answer though. And my implementation does not use a Gallery, obviously.

Jump down to the next bold part for the interesting part

So I got Fling/Swipe/Flick/whatever you want to call it to work a while ago on my application. Inspiration was gathered from a couple of different places, some of them being "basic gesture detection" here on Stack Overflow ( Fling gesture detection on grid layout ), Code Shogun ( http://www.codeshogun.com/blog/2009/04/16/how-to-implement-swipe-action-in-android/ ) and Developing Android ( http://developingandroid.blogspot.com/2009/09/implementing-swipe-gesture.html ), but I do not use a ViewFlipper in my application. When a fling occurs I simply change the tab (wrapping around at the ends).

Now, some of my tabs contain ScrollViews. These ScrollViews obviously respond to up/down scrolls in order to let you view all data inside it, no surprise there. The issue is that it would appear the 'scroll' function of these ScrollViews overwrite my fling gesture. I cannot fling inside a ScrollView (scroll just fine), but it works flawlessly outside them (on the same tab, on other views such as TableRow or whatever).

I had a quick look at http://blog.velir.com/index.php/2010/11/17/android-snapping-horizontal-scroll/ too, which provides a way to implement HorizontalScrollView. But it still handles gestures through a class that extends SimpleOnGestureListener (and overwrites onFling), which is the same implementation as I have (which leads me to believe it won't really help). Source code for ScrollView from Google: http://google.com/codesearch/p?hl=en#uX1GffpyOZk/core/java/android/widget/ScrollView.java&d=3

Is there any way to have my implementation of Swipe and ScrollView working together effortlessly?

This is where the problem lies, I guess. ScrollView.java also uses a method called onTouchEvent and the documentation for the onTouchEvent for Activity states:

"Called when a touch screen event was not handled by any of the views under it. This is most useful to process touch events that happen outside of your window bounds, where there is no view to receive it."

So the ScrollView does "override" it - what do I do? Is there no way to ensure both are checked? My onTouchEvent which is not hit when the onTouchEvent is handled by the ScrollView:

@Override
/** Used for swipe gestures */
public boolean onTouchEvent(MotionEvent event) {
    if (gestureDetector.onTouchEvent(event))
        return true;
    else
        return false;
}

More general source code below, it's probably not very vital. The gestureDetector inside my Tabs class with its associated listener:

    // Gestures
    gestureDetector = new GestureDetector(new MyGestureDetector());
    gestureListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (gestureDetector.onTouchEvent(event)) {
                return true;
            }
            return false;
        }
    };

My gesture class which is a nested class of my Tabs class (which extends TabActivity) - it's the same as any other code you will find on this subject:

/** GestureDetector used to swipe between classes */
class MyGestureDetector extends SimpleOnGestureListener {
    TabHost tabHost = getTabHost(); 

    @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;
            if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                // my tab code
                return true;
            } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                // my tab code
                return true;
            }
        } catch (Exception e) {
            Log.e("MyGestureDetector onFling", e.toString());
        }
        return false;
    }
}
Community
  • 1
  • 1
Codemonkey
  • 3,412
  • 3
  • 20
  • 24
  • 1
    I had the same problem you mentioned here. I finished up with overriding my custom gallery's onInterceptTouchEvent(). It works but is very complicated and ugly. And final solution isn't reusable for diferent set of views. I think the best way is to write my own implementation of scroll view that ignores horizontal scroll events. – Dmitry Ryadnenko Feb 24 '11 at 14:50

3 Answers3

19

I would suggest you have a look at the Google I/O 2010 app source code, as their FlingableTabHost implementation would appear to have solved this problem:

http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/ui/ScheduleActivity.java

I think the key is in extending TabHost and overriding its onInterceptTouchEvent method.

Chris
  • 7,864
  • 1
  • 27
  • 38
Jeff Gilfelt
  • 26,131
  • 7
  • 48
  • 47
  • I implemented the gesture detection in my Tabs class which extends TabActivity though. Should I put the handling in another class entirely which then extends TabHost? I am not really sure how to go about implementing it (lacking overview), heh. – Codemonkey Mar 01 '11 at 08:01
  • Why not implement it exactly as they have done in that project? You should be able to copy their FlingableTabHost inner class definition into your TabActivity class. Just make sure you use a custom layout for the content view that references your custom FlingableTabHost view instead of the normal TabHost, like in: http://code.google.com/p/iosched/source/browse/trunk/res/layout/activity_schedule.xml – Jeff Gilfelt Mar 01 '11 at 08:46
  • Hoorah, I got it to work with some major work-arounds. Implementing Google's FlingableTabHost made it work with the Activities where I have ScrollViews, but stopped it working on some other parts. So I reenabled my onTouchEvent-listener and now it works on both. It's not pretty, but it works. :) Thanks a lot, Jeff. I may want to re-implement my application at some point with a focus on a View-based application (so I'm not constantly jumping between Activities), it shouldn't complicate much in terms of the code I use for this, right? By the way, have some +rep and a claimed bounty. – Codemonkey Mar 01 '11 at 09:45
0

In looking to solve a similar issue I am having, I came across this tid-bit:

eventsInterceptionEnabled: when set to true, this property tells the overlay to steal the events from its children as soon as it knows the user is really drawing a gesture. This is useful when there's a scrollable view under the overlay, to avoid scrolling the underlying child as the user draws his gesture

From the Android Dev site, it talks about using this attribute in the <android.gesture.GestureOverlayView> Root in your layout file.

Kingsolmn
  • 1,868
  • 2
  • 23
  • 39
  • As Jeff suggested, I just went with the way Google had solved it in the ScheduleActivity (and .xml) linked. It's not the prettiest solution as I'm listening on 2 things at the same time, but it works great to ensure that scrolling & swiping work flawlessly. :) – Codemonkey Mar 17 '11 at 21:22
0

For what it's worth, I found the onFling method very unreliable. I override the onScroll method in the SimpleGestureDetector, and define my onInterceptTouchEvent as:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    //Call super first because it does some hidden motion event handling
    boolean result = super.onInterceptTouchEvent(ev);
    if (this.mGestureScanner.onTouchEvent(ev)) return true;
    return result;
}
esilver
  • 27,713
  • 23
  • 122
  • 168