How to capture onTouch events on two overlapping v

2019-04-26 17:04发布

问题:

Layout

!* Container is a relative layout contains two custom views: OuterView1 and InnerView2 * Outer View1 is a custom view, matching the parent’s size (full screen) * Inner View2 is also a custom view, laid on top of OuterView1 overlapping it.

  • Container is a relative layout contains two custom views: OuterView1 and InnerView2
  • Outer View1 is a custom view, matching the parent’s size (full screen)
  • Inner View2 is also a custom view, laid on top of OuterView1 overlapping it.

On both OuterView1 and InnerView2, I want to capture these touch events onSingleTapConfirmed() and onFling(). The area where both OuterView1 and InnerView2 are overlapping, I want the control to be passed to touch event methods of both views.

I tried this:

Container class

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        outerView1.onTouchEvent(ev);
        innerView2.onTouchEvent(ev);
        return false;
    }

OuterView1 class

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gesture.onTouchEvent(event);
        return true;
    }

gesture is an instance of GestureDetector.SimpleOnGestureListener with log statements for onDown(), onFling() and onSingleTapConfirmed() methods

InnerView2 class

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gesture.onTouchEvent(event);
        return true;
    }

gesture is an instance of GestureDetector.SimpleOnGestureListener with log statements for onDown(), onFling() and onSingleTapConfirmed() methods

With this approach, I consistently get callback in onDown() methods of both views. But I don’t see a consistent behaviour on onSingleTapConfirmed() and onFling() methods

When I tapped in the red circle (in the screenshot) three times and I got three different behaviour

  • First try (desired behaviour)

    10-14 09:03:14.155: I/OuterView1(27776): OuterView1.onDown()
    10-14 09:03:14.155: I/InnerView2(27776): InnerView2.onDown()
    10-14 09:03:14.155: W/GestureDetector(27776): [pen gesture] isConsideredDoubleTap - timeout
    10-14 09:03:14.155: I/InnerView2(27776): InnerView2.onDown()
    10-14 09:03:14.460: I/OuterView1(27776): OuterView1.onSingleTapConfirmed()
    10-14 09:03:14.460: I/InnerView2(27776): InnerView2.onSingleTapConfirmed()
    
  • Second try (Only one view gets onSingleTapConfirmed())

    10-14 09:04:11.615: I/OuterView1(27776): OuterView1.onDown()
    10-14 09:04:11.615: I/InnerView2(27776): InnerView2.onDown()
    10-14 09:04:11.615: W/GestureDetector(27776): [pen gesture] isConsideredDoubleTap - timeout
    10-14 09:04:11.615: I/InnerView2(27776): InnerView2.onDown()
    10-14 09:04:11.915: I/OuterView1(27776): OuterView1.onSingleTapConfirmed()
    
  • Third try (One view gets onFling() and another gets onSingleTapConfirmed())

    10-14 09:04:04.180: I/OuterView1(27776): OuterView1.onDown()
    10-14 09:04:04.180: I/InnerView2(27776): InnerView2.onDown()
    10-14 09:04:04.180: W/GestureDetector(27776): [pen gesture] isConsideredDoubleTap - timeout
    10-14 09:04:04.180: I/InnerView2(27776): InnerView2.onDown()
    10-14 09:04:04.255: I/InnerView2(27776): InnerView2.onFling()
    10-14 09:04:04.480: I/OuterView1(27776): OuterView1.onSingleTapConfirmed()
    

Could you help me capture touch events on both OuterView1 and InnerView2?

回答1:

So in the case of overlapping views - touch events are dispatched from the topmost child to the one behind, then another one behind the 2nd child and so on. However, if the topmost child were to consume the touch event (i.e. handle it upon receiving it) then the views underneath that view will not receive the touch events.

If the onTouchEvent returns 'true' then it is telling the view manager that it has handled the touch and does not want the event to be dispatched to other views. If you want the view underneath that view to receive the touch too then you want to return 'false' from the onTouchEvent for the first view, in your case the Innerview.

Also, in your code I noticed you are programmatically dispatching touch events to the 2 views from the container class. This causes the InnerView to receive touch even when ONLY the OuterView is clicked. If you want that functionality then it is ok. If you want InnerView to get the events only when it is touched then you dont want the container class to call onTouchEvent explicitly on those 2 views. The view hierarchy will do it for you.

About receiving onFling or onSingleTapConfirmed on BOTH views - (assuming you want to let the touch events to go through to the views behind as well, you return 'false' from the onTouchEvent for InnerView) the InnerView has already passed the event to the view behind it, so it doesnt get the end of the gesture to realize that a fling just occured and similarly for onSingleTapConfirmed. If you want to still get those then you may have to dispatch touch events programmatically at the end of the gesture as detected by the OuterView. Meaning, if OuterView got a fling gesture then it was a fling for the InnerView too and so programmatically make that gesture happen for the InnerView as well. This is quite complicated and dispatching touches backwards in the hierarchy is not a usual thing.

Hopefully I havent confused you on this. Feel free to ask if I haven't been clear and I will try again.

This will be helpful: Handling TouchEvents in a ViewGroup