当滚动视图太短而无法滚动时,滚动视图不会滑动

发布于 2024-09-15 21:08:32 字数 2747 浏览 8 评论 0原文

我对 Android 应用程序开发还很陌生,我一直在使用 Android 的 SimpleOnGestureListener 和 ViewFlipper 来尝试滑动手势。 ViewFlipper 有 3 个子级,每个都是一个 ScrollView。它们都是在 Activity 加载时动态填充的,之后就不会改变。 ScrollView 是 SimpleOnGestureListener 的附加位置。

这是我正在使用的布局:
+ViewFlipper
++ScrollView(x3,每页一个,每个都有以下内容:)
+++LinearLayout(垂直)
++++文本视图
++++TableLayout(使用 TableRow 动态填充)
++++View

我使用您可以在网上随处找到的通用教程代码扩展了 onFling 方法,并且效果很好 - 除非其中一个 ScrollView 不包含足够的内容来滚动。

我通过重写并调用 SimpleOnGestureListener 的每个方法上的 super 来添加打印到日志,从而将问题范围缩小到触摸检测。

当我在滚动的页面上滑动时,我会得到一些“in onClick”“in onScroll”“in onFling”等内容。在太短而无法滚动的页面上,我会得到“in onClick”“in onShowPress”“in onLongPress” ”,只有当我触摸太短的滚动视图的子级内的内容时才会出现这种情况 - 如果我触摸其他地方,我根本不会收到任何事件。

关于出了什么问题的想法,或者无论 ScrollView 有多大,如何检测滑动手势?

编辑:我已经确定,当我在 Android 2.2 模拟器(而不是我一直使用的 Android 2.1u1 DroidX 模拟器)上运行此程序时,它就会消失。这可以在多个环境中重现。


我对此有更多的见解;当滚动视图包含在翻转器(或 WorkspaceView)中时,似乎不会为每个运动事件调用 onInterceptTouchEvent 。

特别是,我在修改另一个视图类来解决这个同样的问题(这不是脚蹼独有的)时发现的行为如下——请注意,这仅适用于 Android 2.1:

如果滚动视图足够长,可以滚动,则 ACTION_DOWN运动事件被 ScrollView 捕获,并且每个后续的 ACTION_MOVE 事件都会通过翻转器的 onInterceptTouchEvent,在那里它被拦截并进行适当的处​​理。在 android 2.2 中,无论滚动长度如何,都会发生此行为。

回到2.1:如果scrollview不够长,无法滚动,则ACTION_DOWN运动事件不会被scrollview捕获,而是返回到flipper的onTouchEvent。同一手势的所有后续 ACTION_MOVE 事件都会跳过 onInterceptTouchEvent 函数并直接进入 onTouchEvent 函数!

我解决这个问题的方法是采用 onTouchEvent 中 ACTION_MOVE 事件的功能并将其重构为自己的方法。通过这种方式,如果检测到该事件之前未处理,我可以让 onTouchEvent 调用 onInterceptTouchEvent,然后调用该功能。

case MotionEvent.ACTION_MOVE:

                if (touchState == TOUCH_STATE_SCROLLING) {
                    handleScrollMove(ev);
                } else {
    //              Log.d("workspace","caught a move touch event but not scrolling");
                    //NOTE:  We will never hit this case in Android 2.2.  This is to fix a 2.1 bug.
                    //We need to do the work of interceptTouchEvent here because we don't intercept the move
                    //on children who don't scroll.

                    Log.d("workspace","handling move from onTouch");

                    if(onInterceptTouchEvent(ev) && touchState == TOUCH_STATE_SCROLLING){
                        handleScrollMove(ev);
                    }

                }

                break;

这是来自 WorkspaceView.java (Android Workspace.java 的修改,可以在 google 代码上的 andro-views 项目中找到,现在在这里: 在视图之间水平“选项卡”滚动)。在我们收到移动事件并且正在滚动的情况下(只有当我们故意选择拦截它时才会发生 - 即它在拦截函数中设置,所以我们已经使用了拦截函数)我们执行我们想要的移动行为。如果我们在这里收到一个移动事件并且我们没有滚动,那么我们通过 onIntercept 发送回该事件,然后查看我们现在是否设置为滚动。如果是这样,我们将执行该操作。

它并不优雅,但很有效!

I'm pretty new to Android app development, and I've been playing around with swipe gestures using Android's SimpleOnGestureListener and a ViewFlipper. There are 3 children of the ViewFlipper, and each is a ScrollView. They're all dynamically populated when the Activity loads, and they don't change after that. The ScrollView is where the SimpleOnGestureListeners are attached.

Here's the layout I'm using:
+ViewFlipper
++ScrollView (x3, one for each page, each with the following:)
+++LinearLayout (vertical)
++++TextView
++++TableLayout (dynamically populated w/TableRows)
++++View

I extended the onFling method with the common tutorial code you can find anywhere online, and it works great--except when one of the ScrollViews doesn't contain enough content to scroll.

I've narrowed the problem down to touch detection by overriding and calling super on every one of the SimpleOnGestureListener's methods to add a print-to-log.

When I swipe on a page that scrolls, I get something full of "in onClick" "in onScroll" "in onFling" etc. On a page that's too short to scroll, I get "in onClick" "in onShowPress" "in onLongPress", and that's only if I'm touching the content within the too-short scrollview's children--if I touch elsewhere I get no events at all.

Ideas on what's wrong, or how to detect the swipe gesture no matter how big the ScrollView is?

EDIT: I've determined that when I run this on an Android 2.2 emulator, as opposed to the Android 2.1u1 DroidX emulator I've been using, it goes away. This is reproducible across multiple environments.


I have some more insight on this; it seems as though onInterceptTouchEvent is not called for every motion event when a scrollview is contained within a flipper (or a WorkspaceView).

In particular, the behavior I found while modifying another view class to fix this very same issue (it is not unique to flippers) was as follows--note that this is Android 2.1 only:

If the scrollview is long enough to scroll, the ACTION_DOWN motion event is caught by the ScrollView, and every subsequent ACTION_MOVE event goes through onInterceptTouchEvent of the flipper, where it is intercepted and handled appropriately. In android 2.2, this behavior happens regardless of the scroll length.

Back to 2.1: If the scrollview is not long enough to scroll, the ACTION_DOWN motion event is not caught by the scrollview, but instead comes back to the onTouchEvent of the flipper. All subsequent ACTION_MOVE events of the same gesture skip the onInterceptTouchEvent function and go straight to the onTouchEvent function!

The way I resolved this was to take the functionality I had in onTouchEvent for ACTION_MOVE events and refactor it into its own method. In this way, I can have onTouchEvent call onInterceptTouchEvent followed by that functionality if it detects that the event has previously gone unhandled.

case MotionEvent.ACTION_MOVE:

                if (touchState == TOUCH_STATE_SCROLLING) {
                    handleScrollMove(ev);
                } else {
    //              Log.d("workspace","caught a move touch event but not scrolling");
                    //NOTE:  We will never hit this case in Android 2.2.  This is to fix a 2.1 bug.
                    //We need to do the work of interceptTouchEvent here because we don't intercept the move
                    //on children who don't scroll.

                    Log.d("workspace","handling move from onTouch");

                    if(onInterceptTouchEvent(ev) && touchState == TOUCH_STATE_SCROLLING){
                        handleScrollMove(ev);
                    }

                }

                break;

This is from WorkspaceView.java (a modification of Android's Workspace.java, found at the andro-views project on google code, and now here: Horizontal "tab"ish scroll between views ). In the case that we receive a move event, and we are scrolling (which only happens if we have deliberately chosen to intercept it--ie, it's set in the intercept function, so we've been to the intercept function already) we perform the move behavior we desire. If we receive a move event here and we are not scrolling, then we send the event back through onIntercept, and then see if we're now set to scrolling. If so, we perform the action.

It's not elegant, but it works!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

走野 2024-09-22 21:08:32

我需要创建一个扩展 ScrollView 的新类,并使用这个:

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

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

我不知道为什么,但是如果我尝试在dispatchTouchEvent 中返回除 true 以外的任何内容(如果我理解正确,逻辑上的事情就是

return (gestureDetector.onTouchEvent(ev) || super.dispatchTouchEvent(ev)); 

),它不会工作,这确实有效。

I needed to create a new class that extended ScrollView, and used this:

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

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

I have no idea why, but if I try to return anything but true in dispatchTouchEvent (the logical thing would have been to

return (gestureDetector.onTouchEvent(ev) || super.dispatchTouchEvent(ev)); 

if I understand properly), it doesn't work, and this does.

故事未完 2024-09-22 21:08:32

尝试在布局 xml 中为每个 ScrollView 设置 android:fillViewport="true"。这告诉 ScrollView 与它所包含的视图一样大。

Try setting android:fillViewport="true" in your layout xml for each of the ScrollViews. That tells the ScrollView to be as large as the view it's contained in.

无可置疑 2024-09-22 21:08:32

有同样的问题。当 ScrollView 太短而没有滚动条时,您需要拦截 ScrollView 子级上的触摸事件。

Had the same issue. You need to intercept the touch event on the children of the ScrollView when it's too short to have a scrollbar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文