onInterceptTouchEvent never receives action_move
Asked Answered
C

1

9

I have a custom ViewGroup with an override of onInterceptTouchEvent(). It receives ACTION_DOWN but never receives ACTION_MOVE. It is my understanding that, unless it returns "true", it should receive all MotionEvents.

The ViewGroup contains two views, an ImageView and a GridLayout.

My intercept code is:

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev)
  {
    final int action = ev.getAction();
    switch (action & MotionEvent.ACTION_MASK)
    {
      case MotionEvent.ACTION_DOWN:
        logD ("DDV Intercept DOWN");
        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        logD ("DDV Intercept P DOWN"); // logD: shell around Log.d()
        break;
      case MotionEvent.ACTION_MOVE:
        logD ("DDV Intercept MOVE");
        break;
      case MotionEvent.ACTION_UP:
        logD ("DDV Intercept UP");
        break;
      case MotionEvent.ACTION_POINTER_UP:
        logD ("DDV Intercept P UP " + ev.getActionIndex());
        break;
      case MotionEvent.ACTION_CANCEL: 
        logD ("DDV Intercept CANCEL");
        break;
      default:
        logD ("DDV Intercept " + (action & MotionEvent.ACTION_MASK));
    }        
    return false;
  }

I also have code for onTouch that returns false except for one case in ACTION_MOVE; however, it's called only for ACTION_DOWN is called; thus it only return false.

Chemmy answered 18/5, 2014 at 17:54 Comment(0)
T
27

It's a bit more complicated than that. First of all you need to override onTouchEvent() and handle ACTION_DOWN and MOVE events there too. Then the following will happen.

  1. ACTION_DOWN event gets dispatched to onInterceptTouchEvent() first. You should return false from there.
  2. Now there are two cases:
    • If there is no touchable view underneath ACTION_DONW event's location in the view tree, then ACTION_DOWN event and all follow up events get dispatched to onTouchEvent(). You must return true from there. Only then you will receive follow up events sent to onTouchEvent() method. Independently on whether you return true or false, onInterceptTouchEvent() will not receive any follow up events anymore.
    • If there is a touchable view, then all events will be dispatched to onInterceptTouchEvent() (including ACTION_MOVE events). You need to return true from there, after you detected your gesture. Once you return true from here, touchable view will receive ACTION_CANCEL event and all further events will be dispatched to onTouchEvent() method.

Hope this helps.

Thingumabob answered 18/5, 2014 at 18:17 Comment(7)
Yes, I agree with everything you wrote. However - and I think you are saying this - if neither override returns true, onIntercept... should continue to receive all events or, if there are not touchable children, onTouch should. Neither are. Am I missing something?Chemmy
I updated my answer. If there is no touchable child, DOWN dispatched to onTouchEvent() and you must return true from there to get follow up events. If you return false, NO events will be delivered either to onInterceptToucheEvent() or to onTouchEvent().Thingumabob
Ah, that matches my experience. However it is different than the documentation. I was hoping is was missing something else. Thanks for verifying.Chemmy
Right. Documentation on onInterceptTouchEvent() is very misleading.Thingumabob
Just to follow up, I added an OnClickListener to the children. Once I did that, my ViewGroup started receiving ACTION_MOVE during onInterceptTouchEvent(). Crazy! The right Android implementation would be for a ViewGroup to universally receive onInterceptTouchEvent(). Maybe Google can add a new method to do this right in the future!Chemmy
I also thought it's crazy, but once I get the idea, it was all logical. The point is, that if there is no views interested in DOWN event, then Android does not deliver further events. If there is a touchable child, then all events get delivered to that child. This is where onIntercept... is in play. If there is no touchable child, then your view has to express the interest in receiving this event by returning true from onTouchEvent(). In this case there is no reason to call onIntercept... as all events are already delivered to your onTouchEvent and there is nothing to intercept ;)Thingumabob
Well, that sort of makes sense. But unfortunately they didn't think of the case where the child view is for display purposes and doesn't need to handle any touches. In such a case, the parent is prevented from receiving onIntercept... or onTouch unless it returns true right away. But returning true right away means it is not a general purpose container - it has to know whether children handle touch events or not. Admittedly, having no touch-handling children would be a pretty rare case and, I think, easy to overlook or consider unimportant.Chemmy

© 2022 - 2024 — McMap. All rights reserved.