How to capture onTouch events on two overlapping views?
Asked Answered
R

1

8

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?

Rat answered 14/10, 2014 at 4:2 Comment(0)
M
14

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

Messapian answered 14/10, 2014 at 6:12 Comment(4)
Hi Mistwalker, I want to receive onFling(), onSingleTapConfirmed() on both views. I was just trying mulitple options including onInterceptTouchEvent. I'm able to get this working if I have the bottom view's reference at the top most view ` private View outerView = null; public void setOuterView(View outerView) { this.outerView = outerView; } @Override public boolean onTouchEvent(MotionEvent event) { gesture.onTouchEvent(event); if (outerView != null) { outerView.onTouchEvent(event); } return true; }` @Messapian Is there a better way to achieve this?Rat
Code in my previous comment is messed up. Same code is available here: code.google.com/p/ramp-sample/source/browse/android/OverLayView/…Rat
do you want to do the same thing for the 2 views when fling / single tap is detected? Then it would be worthwhile putting them overlapped in a FrameLayout and listening for the gestures on that FrameLayout. Else if you want to do different things when the same touch events are observed on the 2 views then yes, you will have to dispatch touch events programmatically. Not sure if there are other ways to achieve this.Messapian
thanks for your reply. I want to do different things on these two views, I'll go with my approach of distributing the touch event from the top view to the view underneath it.Rat

© 2022 - 2024 — McMap. All rights reserved.