在ScrollView中使用滑动/扔动选项卡切换?

24
我在这个问题上找到的最佳答案(尽管我并没有使用画廊):ScrollView and Gallery interfering - 虽然并没有给出具体的答案。我的实现显然没有使用画廊。
跳到下一个粗体部分以获取有趣的内容。
所以我一段时间前在我的应用程序上使Fling/Swipe/Flick或您想称之为的东西正常工作了。灵感来自于多个地方,其中一些是 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/)和 Developing Android(http://developingandroid.blogspot.com/2009/09/implementing-swipe-gesture.html),但是我没有在我的应用程序中使用 ViewFlipper。当发生 Fling 时,我只是更改选项卡(在两端环绕)。
现在,我的一些选项卡包含 ScrollView。这些 ScrollView 显然会响应上/下滚动以让您查看其中的所有数据,这并不奇怪。 问题在于,似乎这些 ScrollView 的“滚动”功能覆盖了我的 Fling 手势。我无法在 ScrollView 内部进行 Fling(滚动很好),但在外部它可以完美地工作(在同一选项卡中,在其他视图上,比如 TableRow 或任何其他视图)。
我也快速查看了http://blog.velir.com/index.php/2010/11/17/android-snapping-horizontal-scroll/,它提供了一种实现 HorizontalScrollView 的方法。但是它仍然通过扩展 SimpleOnGestureListener (并覆盖 onFling)处理手势,这与我的实现相同(这使我相信它并不能真正帮助解决问题)。来自 Google 的 ScrollView 源代码:http://google.com/codesearch/p?hl=en#uX1GffpyOZk/core/java/android/widget/ScrollView.java&d=3 有没有办法让我实现 Swipe 和 ScrollView 一起轻松工作呢?
我想问题就出在这里。ScrollView.java 也使用一个名为 onTouchEvent 的方法,Activity 的 onTouchEvent 文档则是这样的:
“调用当触摸屏事件不被它下面的任何视图处理时。这在处理发生在窗口边界之外的触摸事件时最有用,因为没有视图接收到它。“
所以 ScrollView 确实“覆盖”了它——我该怎么办?没有办法确保两者都被检查吗?我的 onTouchEvent 没有被 ScrollView 处理时会怎样?
@Override
/** Used for swipe gestures */
public boolean onTouchEvent(MotionEvent event) {
    if (gestureDetector.onTouchEvent(event))
        return true;
    else
        return false;
}

下面是更一般的源代码,它可能不是非常重要。Tabs类中的gestureDetector及其相关的监听器:

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

我的手势类是我的选项卡类的嵌套类(继承自TabActivity) - 它与您在此主题上找到的任何其他代码相同:

/** 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
我曾经遇到了你在这里提到的同样的问题。最终,我通过重写自定义图库的onInterceptTouchEvent()方法来解决它。虽然它能够工作,但是非常复杂和丑陋。而且最终的解决方案对于不同的视图集合并不可重用。我认为最好的方法是编写自己的滚动视图实现,忽略水平滚动事件。 - Dmitry Ryadnenko
3个回答

19

我在扩展了TabActivity的Tabs类中实现了手势检测。但是,我应该将处理完全放在另一个扩展了TabHost的类中吗?我不太确定如何实现它(缺乏概述),呵呵。 - Codemonkey
为什么不像那个项目一样实现它呢?你应该能够将他们的FlingableTabHost内部类定义复制到你的TabActivity类中。只需确保你使用一个自定义布局来引用你的自定义FlingableTabHost视图而不是普通的TabHost,就像这样:http://code.google.com/p/iosched/source/browse/trunk/res/layout/activity_schedule.xml - Jeff Gilfelt
太棒了,我用一些重要的变通方法使它工作了。实现谷歌的FlingableTabHost使其在我有滚动视图的活动中工作,但在其他一些部分停止了工作。因此,我重新启用了我的onTouchEvent监听器,现在它可以在两个方面工作。这不是很漂亮,但它可以工作。 :)非常感谢,杰夫。也许我想重新实现我的应用程序,专注于基于视图的应用程序(所以我不会不断地在活动之间跳转),这不应该在我使用此代码方面造成太多复杂性,对吧?顺便说一下,有一些加分和奖励金。 - Codemonkey

0

就我个人而言,我发现onFling方法非常不可靠。我覆盖了SimpleGestureDetector中的onScroll方法,并将我的onInterceptTouchEvent定义为:

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

0

在寻找解决我遇到的类似问题时,我发现了这个小技巧:

eventsInterceptionEnabled:当设置为true时,该属性告诉叠加层在它知道用户真正绘制手势时立即从其子项中窃取事件。 当叠加层下面有可滚动视图时,这很有用,以避免在用户绘制手势时滚动底层子项

Android开发网站上可以看到,在您的布局文件中的<android.gesture.GestureOverlayView>根中使用此属性。


正如Jeff建议的那样,我采用了Google在链接的ScheduleActivity(和.xml文件)中解决问题的方式。虽然我同时监听了两个事件,这不是最美观的解决方案,但它确保了滚动和滑动的无缝工作。 :) - Codemonkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接