从简单的概述来看,我有一个自定义视图,其中包含一些位图,用户可以拖动并调整大小。
我这样做的方式相当标准,因为我在CustomView中覆盖onTouchEvent,并检查用户是否正在触摸图像等。
当我想将此CustomView放置在ScrollView中时,问题就来了。这是有效的,但ScrollView和CustomView似乎在争夺MotionEvents,即当我试图拖动图像时,它要么移动缓慢,要么视图滚动。
我想我可能需要扩展ScrollView,这样我就可以覆盖onInterceptTouchEvent,并让它知道用户是否在图像的范围内,不要尝试滚动。但是,由于ScrollView在层次结构中的位置更高,我如何访问CustomView的当前状态?
有更好的方法吗?
在这种情况下,Android通常使用长按来开始拖动,因为这有助于消除用户打算拖动项目与滚动项目容器的歧义。但是,如果用户开始拖动项目时有一个明确的信号,那么当您知道用户开始拖动时,可以从自定义视图中尝试getParent().requestDisallowInterceptTouchEvent(true)
。(此处为该方法的文档。)这将防止ScrollView在当前手势结束之前拦截触摸事件。
没有一个解决方案对我来说是"开箱即用"的,可能是因为我的自定义视图扩展了view,而不是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
希望能有所帮助。
有一个名为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;
}