0

I have implemented a ZoomViewGroup, which is capable of scrolling infinitely in all directions and also zooming infinitely while still delivering all touch events correctly offset to its child Views.

But when it comes to multi-touch, only the first pointer is offset correctly, and all others are pointing to a wrong location, and thats because there is the scaling factor I have to take care of. (As pointer 0 has a different offset from its original location than pointer 1 or 2, when scaling_factor != 1.0f)

I am saving my transformation in a Matrix, so it's easy to calculate the coordinates from screen to workspace and back using matrix.mapPoints(..) and the matrix inverse.

When I draw the child views, I can just apply the transformation matrix like this:

protected void dispatchDraw(Canvas canvas)
{
    canvas.save();
    canvas.concat(transformation_matrix);

    super.dispatchDraw(canvas);
    canvas.restore();
}

Same with touch events:

float[] touch_array = new float[2];

public boolean dispatchTouchEvent(MotionEvent event)
{
    touch_array[0] = event.getX();
    touch_array[1] = event.getY();
    transformation_matrix.mapPoints(touch_array);

    event.setLocation(touch_array[0], touch_array[1]);
    return super.dispatchTouchEvent(event);
}

But MotionEvent.setLocation(float x, float y) does actually offset all pointers by the same amount. If we zoomed by 2.0f the offset is different for each pointer, so I have to be able to do something like MotionEvent.setLoction(int pointer_index, float x, float y) for each one individually. Is there anything I can do to achieve this?

bricklore
  • 4,125
  • 1
  • 34
  • 62

1 Answers1

0

EDIT: The only solution I know (and searched for) so far is to create a new MotionEvent with the new coordinates.

--

You can get all pointers (fingers touching) positions trough the methods getX(int pointerId)/getY(int).

You can get all on screen pointers trough the getPointerCount()

So to parse multi-touch you must do something like:

public boolean dispatchTouchEvent(MotionEvent event)
{
    for(int i = 0; i < event.getPointerCount(); i++){
        touch_array[0] = event.getX(i);
        touch_array[1] = event.getY(i);
        transformation_matrix.mapPoints(touch_array);
        MotionEvent copy = MotionEvent.obtainNoHistory(event);
        copy.setLocation(touch_array[0], touch_array[1]);
        boolean handled = super.dispatchTouchEvent(copy);
        copy.recycle();
        if(handled) return true;
    }
    return false;
}

Also note that I created a copy of the MotionEvent object, so childrem can modify it without breaking the position trough the for.

Sorry, just now I noticed your last statement, you must copy the event to a new MotionEvent, so the childrem can parse itself and do its works, copying also will make your code safe of modifications, it's a bad idea to change the MotionEvent directly since it can break a for loop in any dispatch method.

Marcos Vasconcelos
  • 18,136
  • 30
  • 106
  • 167
  • But that way a child cannot recieve multitouch events as you split them up here – bricklore Sep 25 '15 at 18:30
  • Well, I did a overview about multi-touch, you can implement that code in the child dispatchTouchEvent. – Marcos Vasconcelos Sep 25 '15 at 18:31
  • Note that you may want to parse as a PanGesture in the parent or two separete DragsGestures in two different childrem views, you must consider the cases you want to parse to do the best approach. – Marcos Vasconcelos Sep 25 '15 at 18:32
  • No I can't handle this in the childs as I am using this as a root layout for android defined Views which i don't want to modify – bricklore Sep 25 '15 at 18:32
  • I just commented, you need to consider what you want to do with the event, if this is the case you will want to consider the multi-touch and send the raw event received to a child and let it parse as the Android does. – Marcos Vasconcelos Sep 25 '15 at 18:33
  • So you say there is no other possibility? It cannot be done solely in this one viewgroup? – bricklore Sep 25 '15 at 18:36
  • Well, without knowing what your child Views are doing I can't give another solution. They aren't receiving the second pointer? – Marcos Vasconcelos Sep 25 '15 at 18:38
  • A second solution (without knowing your error) is: don't do super.dispatchTouchEvent, and implement it yourself in a for loop, this is a hard work but usually solves touch problems. If the solution can't be achieved then you must override the touchevents on the childrem views. – Marcos Vasconcelos Sep 25 '15 at 18:41
  • Of course they receive the second one. But just think about the zooming. If you have factor 2.0 zoomed in, have one pointer at 0,0 and one at 10,10 then the result is 0,0 for the first and 20,20 for the second. But setLocation only applies the offset from the first pointer to all others too. so the actual result is 0,0 10,10 which is wrong. – bricklore Sep 25 '15 at 18:42
  • Just to mention: Everything is fine for pointer 0, but the second, third, fourth and so on are offset by a wrong amount when zoomed in/out – bricklore Sep 25 '15 at 18:44
  • Bro, the only solution I know (and searched for) so far is to create a new MotionEvent with the new coordinates. – Marcos Vasconcelos Sep 25 '15 at 18:51
  • Hmm so i think I will now also start searching for another solution. – bricklore Sep 25 '15 at 18:56
  • Well, creating the MotionEvent IS the easiest way. – Marcos Vasconcelos Sep 25 '15 at 19:58
  • 1
    Okay so then.. I will try this tomorrow and inform you about the results :) – bricklore Sep 25 '15 at 20:23