I have a custom view, where I have functionality with touch events (swipe etc.). Now it can happen, that this custom view is used within a ScrollableLayout
. The problem then is, that when the user swipes inside my custom view, the parent (ScrollableLayout) will also handle the swipe gesture and so it scrolls, but it should not.
I need something like event.preventDefaults()
from JavaScript.
I override View#onTouchEvent
and return always true
.
I thought, that when I return true from onTouchEvent
this means that the event is consumed and no other view will get the onTouchEvent, but that's wrong.
Can anybody help me?
My View is quite easy to test it:
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;
}
}
I pushed a example android project to github: https://github.com/jjoe64/android-preventing-touch-test
If you touch and scroll inside the red canvas, the ScrollableLayout should not scroll.
To prevent touches from that layout you have several options:
1) set OnTouchListener, which would always return true.
2) override dispatchTouchEvent(MotionEvent event) to always return true
UPD: Okey, normally you would have to set OnTouchListener, which would always return true, as I said previously to prevent other views from being called onTouch
, but with ScrollView it's completely another story.
First of all I will tell how dispatchTouchEvent
works. It starts dispatching event from the root view to it's child views, so that means that dispatchTouchEvent
of parent is called BEFORE dispatchTouchEvent
of it's children.
Then in ViewGroup.dispatchTouchEvent()
there is a piece of code
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;
}
which decides if this touch event should be intercepted, which means if intercepted
flag is true, then this view would intercept all successive event. And in ScrollView
onInterceptTouchEvent
implemented to intercept actions if they are perfroming vertical scrolling.
So my first idea was to set FLAG_DISALLOW_INTERCEPT
to the parent of your custom view to disallow this opportunity of intercepting events, but this idea fails because of the next lines of code in 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();
}
So that means that after the MotionEvent.ACTION_DOWN
all states are cleared (and so do FLAG_DISALLOW_INTERCEPT
is also cleared).
So the final solution is pretty simple
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);
}
}
this would raise FLAG_DISALLOW_INTERCEPT
right after first MotionEvent.ACTION_DOWN
, which would dissallow further interception of future actions by parent.
But note, that if your parent view doing some crazy stuff on MotionEvent.ACTION_DOWN
and intercepting this event then you just can't do anything about it, because your parent's dispatchTouchEvent
are called before your dispatchTouchEvent
.
you have to override parent's onInterceptTouchEvent method
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With