我有自定义视图,其中我有触摸事件的功能(滑动等)。现在可能会发生这种自定义视图在ScrollableLayout
中使用。问题是,当用户在我的自定义视图中滑动时,父(ScrollableLayout)也会处理滑动手势,因此它会滚动,但不应该滚动。
我需要来自JavaScript的event.preventDefaults()
。
我覆盖View#onTouchEvent
并始终返回true
。
我想,当我从onTouchEvent
返回true时,这意味着事件被消耗了,没有其他视图会获得onTouchEvent,但这是错误的。
有人能帮助我吗?
My View很容易测试:
public class PreventingTouchEventView extends View {
public PreventingTouchEventView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.RED);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(200, 200);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}
我将一个示例android项目推送到github: https://github.com/jjoe64/android-preventing-touch-test
如果触摸并滚动红色画布,ScrollableLayout应该不滚动。
答案 0 :(得分:12)
为了防止来自该布局的触摸,您有以下几种选择:
1)设置OnTouchListener,它总是返回true。
2)覆盖dispatchTouchEvent(MotionEvent事件)以始终返回true
UPD: Okey,通常你必须设置OnTouchListener,它总是会返回true,正如我之前所说的那样,为了防止其他视图被调用onTouch
,但是使用ScrollView它完全是另一个故事。
首先,我将告诉dispatchTouchEvent
如何运作。它开始从根视图调度事件到它的子视图,这意味着父视图的dispatchTouchEvent
被称为 BEFORE dispatchTouchEvent
的子视图。
然后在ViewGroup.dispatchTouchEvent()
中有一段代码
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
决定是否应截取此触摸事件,这意味着如果intercepted
标志为真,则此视图将拦截所有后续事件。并在ScrollView
onInterceptTouchEvent
中实施拦截操作,如果他们正在进行垂直滚动。
所以我的第一个想法是将FLAG_DISALLOW_INTERCEPT
设置为自定义视图的父级以禁止这种拦截事件的机会,但由于ViewGroup
中的下一行代码,这个想法失败了:
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
这意味着在MotionEvent.ACTION_DOWN
所有状态被清除之后(FLAG_DISALLOW_INTERCEPT
也被清除)。
所以最终的解决方案很简单
public class PreventingTouchEventView extends View {
public PreventingTouchEventView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.RED);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (getParent() != null && event.getAction() == MotionEvent.ACTION_DOWN) {
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.dispatchTouchEvent(event);
}
}
这会在第一个FLAG_DISALLOW_INTERCEPT
之后立即引发MotionEvent.ACTION_DOWN
,这将阻止父母进一步拦截未来的行动。
但是请注意,如果您的父视图在MotionEvent.ACTION_DOWN
上做了一些疯狂的事情并拦截了这个事件,那么您就无法对此做任何事情,因为您父母的dispatchTouchEvent
会在dispatchTouchEvent
之前被调用1}}。
答案 1 :(得分:1)
你必须覆盖父的onInterceptTouchEvent方法