Andrii Chernenko
Andrii Chernenko

Reputation: 10204

Custom ViewGroup.dispatchTouchEvent() doesn't work correctly

I'm developing custom ViewGroup. It is some kind of radial menu (similar to options menu in android camera app).

I managed to lay out and draw children properly. Another thing I had to do is to override touch events dispatching:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    for (int i=0; i<getChildCount(); i++) {
        CircularView child=(CircularView) getChildAt(i);
        if (child.viewMetrics.containsPoint(ev.getX(), ev.getY())) {
            child.dispatchTouchEvent(ev);
            return true;
        }
    }
    return false;
}

I iterate through children and dispatch event if event coordinates belong to any of them (child.viewMetrics.containsPoint() method call, double checked, works correctly).

Seems to work, but it's not perfect. I noticed the problem: when I tap, hold my finger and move it even slightly, the click is interrupted. Here's the demo.

I looked at superclass implementation, and there's a lot of code. My first thought was to copy it, but it depends on many private classes.

How do I make sure touch events dispatching works correctly? (not only clicks, but all other gestures as well?) If there's no easy way to do it, I would appreciate any comments or advice on porting the default implementation or any links to useful information on the topic.

Upvotes: 4

Views: 5088

Answers (1)

Marcos Vasconcelos
Marcos Vasconcelos

Reputation: 18276

When dispatching touchEvents, you should offset the location to the view left/top.

Cause a click on 800x400 of a scrollview (while the scrollX is = 200) means that you are touching a view that are at 1000x400, then you know which view has been touched and it's bounds are like ltrb(950, 0, 1050, 600), to dispatch it properly, you should offset the value of x,y in the event you are dispatching, this means you send a MotionEvent(50,400) to the child view.

The view will handle properly the touchEvent and dispatch it to children or handle itself.

And a tip is, sometimes a View while dispatching will ofsset the event by some value, and the next MotionEvent will be no longer equals as the original after a loop, so you should send a copy to the children.

In resume, it's kind of this:

MotionEvent cp = MotionEvent.obtain(event);
cp.offset(-child.getLeft(), -child.getTop());
boolean consumed = child.dispatchTouchEvent(cp);
cp.recycle();
return consumed;

I had override dispatchTouchEvent/onTouchEvent of several classes and this does works properly.

Also: I did watch the youtube video, may some of your views are intercepting the touch event and sending a ACTION_CANCEL to your views, that's why you loose the events. (That happened to me, that's why I override dispatch/onTouch of all View classes)

Upvotes: 13

Related Questions