Reputation: 18276
I has the following class that represents a View that is touchable and draw a Slide Bar.
public class SlideBar extends View {
private int progress;
private int max;
private Paint background;
private Paint upground;
private RectF bar;
private boolean firstDraw;
public SlideBar(Context context, AttributeSet attrs) {
super(context, attrs);
progress = 0;
upground = new Paint();
upground.setColor(Color.parseColor("#C2296C"));
background = new Paint();
background.setColor(Color.parseColor("#777777"));
}
private void onFirstDraw() {
max = getWidth();
bar = new RectF(0, 19, max, 21);
}
public void onDraw(Canvas canvas) {
if (!firstDraw) {
onFirstDraw();
progress = max;
firstDraw = true;
}
canvas.save();
canvas.drawRoundRect(bar, 5, 5, background);
canvas.drawCircle(progress, 20, 9, upground);
canvas.restore();
}
public void setValue(int value) {
progress = value;
}
public boolean onTouchEvent(MotionEvent evt) {
System.out.println(evt.getAction());
progress = (int) evt.getX();
invalidate();
return false;
}
}
But when touching and dragging it, I receive a ACTION_DOWN, some ACTION_MOVEs then receive a ACTION_CANCEL and no further events.
Why it's happens? I don't want to cancel the event and enable it to keep dragging bar.
Upvotes: 30
Views: 28178
Reputation: 34265
Android ACTION_CANCEL while touching
Another alternative way it is:
You have ViewGroup
with View
inside.
ViewGroup
's method onInterceptTouchEvent()
return false
for ACTION_DOWN
and true
for other casesView
always returns true
in onTouch
or onTouchEvent
ACTION_DOWN
, ACTION_MOVE
...As a result you will see the next flow
ACTION_DOWN
iteration:
ViewGroup dispatchTouchEvent >start< ev = ACTION_DOWN
ViewGroup onInterceptTouchEvent false
View dispatchTouchEvent >start< ev = ACTION_DOWN
View onTouch true
View dispatchTouchEvent >finish< true
ViewGroup dispatchTouchEvent >finish< true
ACTION_MOVE
iteration:
ViewGroup dispatchTouchEvent >start< ev = ACTION_MOVE
ViewGroup onInterceptTouchEvent true //<- start intercepting
View dispatchTouchEvent >start< ev = ACTION_CANCEL //<- View is notified that control was intercepted
View onTouch true
View dispatchTouchEvent >finish< true
ViewGroup dispatchTouchEvent >finish< true
ACTION_MOVE
iteration:
ViewGroup dispatchTouchEvent >start< ev = ACTION_MOVE
ViewGroup dispatchTouchEvent >finish< false
Upvotes: 1
Reputation: 21
Need to disallow parent view to intercept the touch event:
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
(parent as? ViewGroup?)?.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_UP -> {
(parent as? ViewGroup?)?.requestDisallowInterceptTouchEvent(false)
}
else -> {
}
}
return true
}
Upvotes: 2
Reputation: 42849
An ACTION_CANCEL
happens when a parent view takes over control of one of its children views.
Take a look at the documentation around ViewGroup.onInterceptTouchEvent(MotionEvent) method. From the link:
onTouchEvent()
method to handle; this means you should implement onTouchEvent()
to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent()
, you will not receive any following events in onInterceptTouchEvent()
and all touch processing must happen in onTouchEvent()
like normal.onTouchEvent()
.ACTION_CANCEL
, and all further events will be delivered to your onTouchEvent()
method and no longer appear hereUpvotes: 32
Reputation: 7442
This will happen when parent container will intercept your touch event. Any ViewGroup that overrides ViewGroup.onInterceptTouchEvent(MotionEvent) can do that (ScrollView or ListView for instance).
Proper way to deal with this is to call ViewParent.requestDisallowInterceptTouchEvent(boolean) method on your parent view once you think you need to keep the motion event.
Here's a quick example (attemptClaimDrag method is taken from android source code):
/**
* Tries to claim the user's drag motion, and requests disallowing any
* ancestors from stealing events in the drag.
*/
private void attemptClaimDrag() {
//mParent = getParent();
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(true);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (iWantToKeepThisEventForMyself(event)) {
attemptClaimDrag();
}
//your logic here
} else {
//your logic here
}
}
Upvotes: 62