gpt4 book ai didi

android - 如何防止 TouchEvent 滚动

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:43:26 27 4
gpt4 key购买 nike

我有一个自定义 View ,其中我具有触摸事件功能(滑动等)。现在可能会发生这种情况,即在 ScrollableLayout 中使用此自定义 View 。那么问题是,当用户在我的自定义 View 内滑动时,父级 (ScrollableLayout) 也会处理滑动手势,因此它会滚动,但它不应该滚动。

我需要来自 JavaScript 的类似 event.preventDefaults() 的东西。

我覆盖 View#onTouchEvent 并始终返回 true。我想,当我从 onTouchEvent 返回 true 时,这意味着该事件已被消耗并且没有其他 View 将获得 onTouchEvent,但这是错误的。

谁能帮帮我?

我的 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

如果您在红色 Canvas 内触摸和滚动,ScrollableLayout 应该滚动。

最佳答案

要防止触摸该布局,您有多种选择:

1) 设置 OnTouchListener,它总是返回 true。

2) 覆盖 dispatchTouchEvent(MotionEvent event) 总是返回 true

UPD: 好吧,通常你必须设置 OnTouchListener,它总是返回 true,正如我之前所说,以防止其他 View 被调用 onTouch,但是ScrollView 完全是另一个故事。

首先,我将介绍 dispatchTouchEvent 的工作原理。它开始从 Root View 向其 subview 分派(dispatch)事件,这意味着父 View 的 dispatchTouchEvent 被其 subview 的 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 标志为真,则此 View 将拦截所有后续事件。并在 ScrollView onInterceptTouchEvent 中实现拦截正在执行垂直滚动的操作。

所以我的第一个想法是将 FLAG_DISALLOW_INTERCEPT 设置为自定义 View 的父级,以禁止拦截事件的机会,但由于 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);
}
}

这会在第一个 MotionEvent.ACTION_DOWN 之后立即引发 FLAG_DISALLOW_INTERCEPT,这将禁止父级进一步拦截 future 的操作。

但请注意,如果您的 parent 认为在 MotionEvent.ACTION_DOWN 上做了一些疯狂的事情并拦截了此事件,那么您对此无能为力,因为您 parent 的 dispatchTouchEvent 在您的 dispatchTouchEvent 之前被调用。

关于android - 如何防止 TouchEvent 滚动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17837711/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com