2
votes

I am drawing a complicated shape using canvas.drawPath. The result of these drawPath methods is something like a map which is bigger than the screen. I would like the user to do the following: move the map using one finger. Or rotate it around the center of the screen using 2 fingers. Basically it should behave exactly like Google Maps only without scaling. So far I was able to get the desired movement and rotation:

private void handleTouch(MotionEvent event)
{
    switch(event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_POINTER_DOWN:
        {
            rotate=true;
            move=false;
            a=Math.toDegrees(-Math.atan2(event.getX(0)-event.getX(1),event.getY(0)-event.getY(1)));
            if(a>360)a=a-360;
            if(a<-360)a=a+360;
            da=a-angle;
            if(da>360)da=da-360;
            if(da<-360)da=da+360;

        }
        break;
        case MotionEvent.ACTION_POINTER_UP:
        {
            rotate=false;
            move=false;
            x = (int)event.getX();
            y = (int)event.getY();
            dx = x - translate.x;
            dy = y - translate.y;
        }
        break;
        case MotionEvent.ACTION_DOWN:
        {
            move=true;
            x = (int)event.getX();
            y = (int)event.getY();
            dx = x - translate.x;
            dy = y - translate.y;
        }
        break;
        case MotionEvent.ACTION_MOVE:
        {
            if(rotate && event.getPointerCount()==2)
            {
                angle=Math.toDegrees((-Math.atan2(event.getX(0)-event.getX(1),event.getY(0)-event.getY(1))))-da;
                if(angle>360)angle=angle-360;
                if(angle<-360)angle=angle+360;
                vmap.invalidate();

            }
            else if(move==true)
            {
                translate.set((int)event.getX() - dx,(int)event.getY() - dy);
                //trans.setTranslate(translate.x,translate.y);

                vmap.invalidate();

            }

        }
        break;
        case MotionEvent.ACTION_UP:
        {
            //your stuff
        }
        break;

    }

The problem is correctly applying them together. I can easily make the rotate around its own center if I first translate and then rotate around the center coordinate of the map. However if I try to rotate around the center of the screen things start to get complicated. Lets say I moved the canvas 20 pixels left and then rotated it 30 degrees, then moved 50 pixels down and then rotated it another 50 degrees. If I try to add the two angles together during the second rotation, I will now be rotating around a different coordinate, meaning that as soon as I do the new rotation the map is going to suddenly jump.

I looked into using matrices, but so far that didn't help.

1

1 Answers

0
votes

Take a look at the Matrix class. It lets you transform ImageViews in many ways. For this problem, I created a test project that included the members:

ImageView imageView;
Matrix imageMatrix = new Matrix();
PointF screenCenter = new PointF();

Then, in onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    screenCenter.x = metrics.widthPixels/2;
    screenCenter.y = metrics.heightPixels/2;

    imageView = (ImageView) findViewById(R.id.image);
    imageView.setScaleType(ImageView.ScaleType.MATRIX);
    imageMatrix.set(imageView.getImageMatrix());
}

The screenCenter object now contains the coordinates of the center of the screen, which will be used later. The imageView scale type has been set to MATRIX, and the imageMatrix has been set with the starting matrix that was applied to the view when we changed the scale type.

Now, when you translate you modify the imageMatrix, then apply the new matrix to the view with setImageMatrix():

void translate(float x, float y) {
    imageMatrix.postTranslate(x, y);
    imageView.setImageMatrix(imageMatrix);
}

Rotation works the same way, but we need the coordinates of the center of the screen:

void rotate(float angle) {
    imageMatrix.postRotate(angle, screenCenter.x, screenCenter.y);
    imageView.setImageMatrix(imageMatrix);
}

This works on my emulator, translate moves the expected direction and rotate always moves around the center of the screen, no matter how it's translated. Admittedly, it's a test project and it doesn't look very pretty.

OpenGL ES is included in the Android framework, and you might get better graphics performance if you go that route. If you do, then this answer might be helpful.