Swipe/Fling tab-changing in conjunction with Scrol

2019-01-10 08:42发布

问题:

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;
    }
}

回答1:

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.



回答2:

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.



回答3:

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;
}