Android: Difference Between Onintercepttouchevent and Dispatchtouchevent

Handling Touch Events - onInterceptTouchEvent and onTouchEvent

Note that onInterceptTouchEvent() is a method from the ViewGroup class, and not from Activity.

You can achieve the desired behavior by moving your logic from onInterceptTouchEvent() to dispatchTouchEvent(MotionEvent ev). Remember to call the superclass implementation of dispatchTouchEvent(MotionEvent ev) to handle the events that should be handled normally.

Also note that you should consider a movement to be a swipe only when the delta is bigger than the system constant for touch slop. And I suggest making sure that the user is swiping in the direction you want by testing yDelta / 2 > xDelta instead of yDelta > xDelta.

public class Game extends Activity {
private int mSlop;
private float mDownX;
private float mDownY;
private boolean mSwiping;

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.game_activity);

ViewConfiguration vc = ViewConfiguration.get(this)
mSlop = vc.getScaledTouchSlop();

//other code....
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = ev.getX();
mDownY = ev.getY();
mSwiping = false;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if(mSwiping) {
swipeScreen(); //if action recognized as swipe then swipe
}
break;
case MotionEvent.ACTION_MOVE:
float x = ev.getX();
float y = ev.getY();
float xDelta = Math.abs(x - mDownX);
float yDelta = Math.abs(y - mDownY);

if (yDelta > mSlop && yDelta / 2 > xDelta) {
mSwiping = true;
return true;
}
break;
}

return super.dispatchTouchEvent(ev);
}
}

One touch in dispatchTouchEvent registered multiple times

When using dispatchTouchEvent , you take all touches in your activity, if you want only detect one touch, you have to filter the touch by its type, you can do this using the MotionEvent parameter.

if( ev.getAction() == MotionEvent.ACTION_UP){
//launch activity video player
}

How are Android touch events delivered?

From Activity viewpoint:

Touch events are delivered first to Activity.dispatchTouchEvent. It's where you may catch them first.

Here they get dispatched to Window, where they traverse View hierarchy, in such order that Widgets that are drawn last (on top of other widgets) have chance to process touch in View.onTouchEvent first. If some View returns true in onTouchEvent, then traversal stops and other Views don't receive touch event.

Finally, if no View consumes touch, it's delivered to Activity.onTouchEvent.

That's all your control. And it's logical that what you see drawn on top of something else, has chance to process touch event before something drawn below it.

How to use dispatchTouchEvent?

You can try calling yourView.dispatchTouchEvent(MotionEvent.obtain);

Custom dispatchTouchEvent causes View.OnClickListener to be ignored

Problem was not having my detail view inserted in ViewGroup. When mDetailView became child of my custom ViewGroup, problem was solved.

Android onInterceptTouchEvent doesn't get action when have child view

The solution relates to this answer, as it is a result of the standard MotionEvent handling.

In my code, when I touch the child LinearLayout, the parent CustomViewGroup doesn't intercept the MotionEvent as it returns false, but my child LinearLayout doesn't consume the MotionEvent either, so the MotionEvent is returned to the parent's onTouchEvent, not onInterceptTouchEvent.

On the other hand, when I touch my child ScrollView, it consumes the MotionEvent whether scrolling is enabled or disabled.

==> I think because Android doesn't generate a MotionEvent again until the original one is either consumed or finished, so the parent CustomViewGroup doesn't get the ACTION_MOVE MotionEvent via onInterceptTouchEvent, instaed it is piped to onTouchEvent when a child doesn't consume the MotionEvent.

I found two solutions

Solution one

Forcibly make my LinearLayout consume the MotionEvent. This solution is available only when the child LinearLayout has no touchable View, ViewGroup or Widget. Like this:

LinearLayout mLinearLayout = (LinearLayout)findViewById(R.id.child_linear);
mLinearLayout.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});

Solution two

Process the MotionEvent returning from a child in my CustomViewGroup's onTouchEvent. Like this: (Just add else if case in onTouchEvent).

case MotionEvent.ACTION_MOVE: {
if (mIsBeingDragged) {
mLastMotionY = event.getY();
mLastMotionX = event.getX();
pullEvent();
return true;
}
else if (isReadyForPull()) {
final float y = event.getY(), x = event.getX();
final float diff, oppositeDiff, absDiff;
diff = y - mLastMotionY;
oppositeDiff = x - mLastMotionX;
absDiff = Math.abs(diff);
ViewConfiguration config = ViewConfiguration.get(getContext());

if (absDiff > config.getScaledTouchSlop() && absDiff >
Math.abs(oppositeDiff) && diff >= 1f) {
mLastMotionY = y;
mLastMotionX = x;
mIsBeingDragged = true;
}
}
break;
}

While solution 1 is a quick fix for certain situations, solution two is the most flexible and reliable.

Android-dispatchTouchEvent is not catching ACTION_CANCEL

@pskink's answer is correct. i.e. ACTION_CANCEL is a system event.

"ACTION_CANCEL occurs when the parent takes possession of the motion, for example when the user has dragged enough across a list view that it will start scrolling instead of letting you press the buttons inside of i.

http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent%28android.view.MotionEvent%29



Related Topics



Leave a reply



Submit