androidandroid-touch-event

TouchEvents on Viewgroups: Catch MOVE_OVER on views


I'm trying to figure out how to arrange my onInterceptTouchEvent and onTouchEvent overrides in various classes.

 ButtonContainer            
          |                    
          |-----------+        
       Button      BitContainer
                            |  
                ------------|  
              View1       View2

ButtonContainer and BitContainer are RelativeLayouts, Button, View1 and View2 are all imageviews with a drawable circle background. View1 and View2 are initially invisible.

The goal is to have ButtonContainer respond to the initial ACTION_DOWN event by making view1 and view2 visible. On the ACTION_MOVE event if the touch moves from Button over to View1, then the View1 onTouchEvent method should be called.

I thought I would be able to use onInterceptTouchEvent on ButtonContainer and set it to true in the case of ACTION_DOWN but false in all other cases. Shouldn't this prevent the ACTION_MOVE event from going straight to the ButtonContainer onTouch and make it traverse the tree instead? If so, then the ACTION_MOVE event would follow the usual event flow and be detected by the other views, i.e View 1 & 2.

No matter what I do, I can't get the onTouchEvent methods in view1 or 2 to respond to the ACTION_MOVE gesture.

Is the event likely to be getting caught elsewhere? I noticed that if View 1 is visible and below the TouchEvent location on the ACTION_DOWN even, it detects subsequent ACTION_MOVE events... but if the TouchEvent occurs over view 2 or button, then view1 does not respond to ACTION_MOVE.

EDIT: Ok, so I read a bit more and it seems that if an object 'doesnt consume the ACTION_DOWN event, then it won't consume any further events (for efficiency reasons)'...

I assume consume means 'is directly beneath' rather than 'is part of the ViewGroup'. Is that correct? Is there a way to pass the event to View1 so that it can be notified of ACTION_MOVE?


Solution

  • For anyone else facing this problem, the trick was to do the event dispatching manually.

    Button1 returns true from the OnTouchEvent if action = ACTION_DOWN. When it is ACTION_MOVE, then return false.

    ButtonContainer returns true on onInterceptTouchEvent when action is ACTION_MOVE and then calls the dispatchTouchEvent of BitContainer, which calls dispatchTouchEvent on all t's child views using a for loop

    From there, you can now write a method in each of your child views to check if the x position of the touchevent is within their own getLocationOnScreen() coordinates.

    Here is the onInterceptTouch event on ButtonContainer, and dispatchTouchEvent on BitContainer as an example:

    Button Container

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        if (ev != null) {
            if (ev.action == MotionEvent.ACTION_MOVE) {
                bitContainer.dispatchTouchEvent(ev)
                return true
            }
        }
        return super.onInterceptTouchEvent(ev)
    }
    

    BitContainer

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        if (ev != null) {
            if (ev.action == MotionEvent.ACTION_MOVE) {
                for (i in 0 until this.getChildCount()) {
                    this.getChildAt(i).dispatchTouchEvent(ev)
                }
    
            }
        }
        return super.dispatchTouchEvent(ev)
    }