I'm trying to have a scrolling MapView inside of a RecyclerView, therefore I'm setting requestDisallowInterceptTouchEvent() before and after the TouchEvent.
The odd thing is: this does work if I set it in the dispatchTouchEvent() method, but it doesn't work if I do the same in the onTouchEvent() method.
Can somebody explain why I cannot set this in onTouchEvent()?
Working:
public class WorkingScrollableListItemMapView extends MapView {
// constructors
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// Stop parents from handling the touch
this.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
// Allow parents from handling the touch
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return super.dispatchTouchEvent(ev);
}
}
Not working:
public class NotWorkingScrollableListItemMapView extends MapView {
// constructors
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// Allow parents from handling the touch
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_DOWN:
// Stop parents from handling the touch
this.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return super.onTouchEvent(ev);
}
}
Call sequence for handling an event are somewhat in this order:
onInterceptTouchEvent, onDispatchTouchEvent, dispatchTouchEvent, onTouchEvent.
That, to me, indicates that the onTouchEvent is the very last step in processing an event. It would be too late to manipulate where & whom handles the event at the very last step. What does the source code say if you look at the earlier methods for handling the event?
Related
I have a custom view which acts as a button. I am drawing all the canvas myself. Now I'm making an outline when ACTION_DOWN and remove it after ACTION_UP or ACTION_CANCEL.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("test", "ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.e("test", "ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e("test", "ACTION_CANCEL");
break;
}
return true;
}
This can work for me, except now it is blocking another gesture behind this view which is detecting ACTION_MOVE (scroll left).
If I return false, then it is working fine but now ACTION_UP is not called.
I want to call ACTION_UP if finger is lifted, but pass events down otherwise.
Have you tried overriding dispatchTouchEvent?
https://developer.android.com/reference/android/view/View.html#dispatchTouchEvent(android.view.MotionEvent)
UPDATE:
So touch events are a bit of a beast. The rundown of it is this...
They bubble up at first from your root container in your Activity. This is done by calling dispatchTouchEvent and then onInterceptTouchEvent assuming intercepting wasn't blocked by a child view.
If no view intercepts the event, it will bubble to the leaf node (such as a button) where onTouch is called. If the node doesn't handle it (returns true) its parent gets a chance and so on.
This means that you can use dispatchTouchEvent or onInterceptTouchEvent to spy on touch events without changing the behavior. Unless you're actually going to intercept the event I suggest using dispatchTouchEvent as it's guaranteed to run whereas intercepting may be blocked (example: DrawerLayout will intercept touch events near the edge in order to open the drawer).
So the final result is:
public class MyView extends Button {
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
Log.e("test", "ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.e("test", "ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e("test", "ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(event);
}
}
UPDATE:
Sorry, so I've been under the impression for some reason (mostly my poor reading) that we were dealing with the parent. Here's what I would do...
Go ahead and implement onTouch and return true to consume all the events. This means that any touch events that start on your view will be eaten up. What we'll do then is translate the point to the parent's coordinate space and manually pass the touch event up, it'll look like this inside your custom view...
private boolean passingTouchEventToParent = true;
final private Rect hitRect = Rect();
#Override
public boolean onTouch(MotionEvent event) {
// Handle your custom logic here
final ViewParent viewParent = getParent();
if (passingTouchEventToParent &&
viewParent != null &&
viewParent instanceof View) {
// Gets this view's hit rectangle in the parent's space
getHitRect(hitRect);
event.offsetLocation((float) hitRect.left, (float) hitRect.top);
passingTouchEventToParent = viewParent.onTouchEvent(event);
}
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
// Motion event finished, reset passingTouchEventToParent
passingTouchEventToParent = true;
}
return true;
}
I have a custom FrameLayout which overrides this method:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
return interceptTouchEvents;
}
return false;
}
I then have a touch listener which I set as the TouchListener of this FrameLayout. At some point inside the onTouch method I'd like to forward this event to a children.
you can use below code for dispatch touch event to view,
// Dispatch touch event to view
view.dispatchTouchEvent(motionEvent);
Hope this will help.
Thanks
I have an issue when using onSingleTapUp of GestureDetector and onTouchEvent of the View together. Both onSingleTapUp and ACTION_DOWN/UP sections of the onTouchEvent manipulate same object set before drawing on the canvas.
I want to stop onTouchEvent continuing if it is clearly a single tap. I am not sure how to avoid this. Should I use synchronize instead?
#Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
//code here
case MotionEvent.ACTION_MOVE:
//code here
case MotionEvent.ACTION_UP:
//code here
}
//try this:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
return gestureDetector.onTouchEvent(ev);
}
So I have an autoScrolling scrollView in my class and I want to intercept the user onTouch event so that it stops the scrolling when they click on the scrollView. How will I implement this functionality? I know it has something to do with Overriding the onTouchEvent function, but when I Override this it doesn't work.
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_POINTER_1_DOWN:
stopAutoScrolling();
case MotionEvent.ACTION_POINTER_1_UP:
startAutoScrolling();
}
return true;
}
I believe when you return true, you are saying you have handled the onTouchEvent. If you want the normal touch behavior to occur do something like:
return super.onTouchEvent(ev);
That way you call the native code which in your case would perform the scrolling as normal.
I'm trying to create a custom GridView but i'm having troubles with the touch listeners.
What i want to do:
Create a GridView with custom Views.
Longpress on an item so it becomes 'editable'.
Drag the view horizontal or vertical to move it's position in the GridView.
Here's where i'm having trouble:
I'm implementing GestureDetector.OnGestureListener for the longpress functions, because for some reason using the gridview.setOnItemLongClickListener() isn't working when overriding the onTouchEvent() of the GridView itself (Which i need for the dragging part). So everything is fine at this point. Now i only need to know when the longpress is finished. So i though: "Well this shouldn't be hard." I couldn't have be more wrong. I've fiddled around for quite some time with this and it looks like using different touch events isn't helping me :/
When stepping through the onTouchEvent() i noticed that only 1 action is given: MotionEvent.ACTION_DOWN. So what am i doing wrong? i need the MotionEvent.ACTION_UP...
Found the culprit:
i was doing something like this
#Override
public boolean onTouchEvent(MotionEvent event) {
// Give everything to the gesture detector
boolean retValue = gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE :
onMove();
break;
case MotionEvent.ACTION_UP :
onUp();
break;
case MotionEvent.ACTION_CANCEL:
onCancel();
break;
}
return retValue;
}
i think retValue was always returning false so no other events were triggered.
this fixed the issue:
#Override
public boolean onTouchEvent(MotionEvent event) {
// Give everything to the gesture detector
gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE :
onMove();
break;
case MotionEvent.ACTION_UP :
onUp();
break;
case MotionEvent.ACTION_CANCEL:
onCancel();
break;
}
return true;
}