覆盖onTouchEvent与ScrollView竞争

时间:2011-05-31 23:33:43

标签: android user-interface drag-and-drop scrollview

从简单的概述我有一个自定义视图,其中包含用户可以拖动并调整大小的一些位图。

我这样做的方式是相当标准的,因为我在我的CustomView中重写onTouchEvent并检查用户是否在图像中触摸等。

当我想将此CustomView放在ScrollView中时,我的问题出现了。这可行,但ScrollView和CustomView似乎竞争MotionEvents,即当我尝试拖动图像时,它会缓慢移动或视图滚动。

我想我可能需要扩展ScrollView,以便我可以覆盖onInterceptTouchEvent并让它知道用户是否在图像的范围内而不是尝试滚动。但是,因为ScrollView在层次结构中更高,我将如何访问CustomView的当前状态?

有更好的方法吗?

3 个答案:

答案 0 :(得分:40)

正常情况下,Android会使用长按来开始拖动这类情况,因为它有助于消除用户打算拖动项目时的歧义,而不是滚动项目的容器。但是,如果您在用户开始拖动项目时有明确的信号,请在知道用户开始拖动时从自定义视图中尝试getParent().requestDisallowInterceptTouchEvent(true)。 (此方法的文档here。)这将阻止ScrollView拦截触摸事件,直到当前手势结束。

答案 1 :(得分:8)

找不到任何解决方案对我来说都是“开箱即用”,可能是因为我的自定义视图扩展了查看,而不是 ViewGroup ,因此我无法实现onInterceptTouchEvent

同时打电话给getParent().requestDisallowInterceptTouchEvent(true)投掷NPE,或根本不做任何事情。

最后这就是我解决问题的方法:
当您的视图处理该事件时,在您的自定义onTouchEvent内部调用requestDisallow...。例如:

@Override
public boolean onTouchEvent(MotionEvent event) {
    Point pt = new Point( (int)event.getX(), (int)event.getY() );
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        if (/*this is an interesting event my View will handle*/) {
            // here is the fix! now without NPE
            if (getParent() != null) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }

            clicked_on_image = true;
        }
    } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
        if (clicked_on_image) {
            //do stuff, drag the image or whatever
        }
    } else if (event.getAction() == MotionEvent.ACTION_UP) {
        clicked_on_image = false;
    }
    return true;
}

现在我的自定义视图工作正常,处理一些事件并让scrollView捕获我们不关心的事件。在此处找到解决方案:http://android-devblog.blogspot.com.es/2011/01/scrolling-inside-scrollview.html

希望它有所帮助。

答案 2 :(得分:0)

有一个名为MotionEvent.ACTION_CANCEL的Android事件(值= 3)。我所做的只是覆盖我的自定义控件的onTouchEvent方法并捕获此值。如果我发现这种情况,那么我会做出相应的反应。

以下是一些代码:

@Override
public boolean onTouchEvent(MotionEvent event) {
    if(isTouchable) {
        int maskedAction = event.getActionMasked();         
        if (maskedAction == MotionEvent.ACTION_DOWN) {
            this.setTextColor(resources.getColor(R.color.octane_orange));
            initialClick = event.getX();
        } else if (maskedAction == MotionEvent.ACTION_UP) {
            this.setTextColor(defaultTextColor);
            endingClick = event.getX();
            checkIfSwipeOrClick(initialClick, endingClick, range);
        } else if(maskedAction == MotionEvent.ACTION_CANCEL)
            this.setTextColor(defaultTextColor);
    }
    return true;
}