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
?
UPDATE:
So touch events are a bit of a beast. The rundown of it is this...
dispatchTouchEvent
and then onInterceptTouchEvent
assuming intercepting wasn't blocked by a child view.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;
}