如何防止TouchEvent滚动

app*_*ter 3 android

我有一个自定义视图,我有触摸事件的功能(滑动等).现在可能会发生这种自定义视图在a中使用ScrollableLayout.问题是,当用户在我的自定义视图中滑动时,父(ScrollableLayout)也将处理滑动手势,因此它会滚动,但它不应该滚动.

我需要像event.preventDefaults()JavaScript 这样的东西.

View#onTouchEvent总是覆盖并返回true.我想,当我从onTouchEvent这里返回true 意味着事件被消耗而没有其他视图会获得onTouchEvent,但这是错误的.

有谁能够帮我?

我的视图很容易测试它:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我将一个示例android项目推送到github:https: //github.com/jjoe64/android-preventing-touch-test

如果触摸,滚动红帆布里面,ScrollableLayout应该不会滚动.

Des*_*ert 11

要防止来自该布局的触摸,您有以下几种选择:

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;
}
Run Code Online (Sandbox Code Playgroud)

它决定是否应截取此触摸事件,这意味着如果interceptedflag为true,则此视图将截取所有后续事件.并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();
}
Run Code Online (Sandbox Code Playgroud)

这意味着在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将FLAG_DISALLOW_INTERCEPT在第一次之后立即提高MotionEvent.ACTION_DOWN,这将不允许父母进一步拦截未来的行动.

但是请注意,如果你的父视图做了一些疯狂的事情MotionEvent.ACTION_DOWN并且拦截了这个事件,那么你就无法做任何事情,因为你的父母dispatchTouchEvent在你之前被调用了dispatchTouchEvent.